<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="JUST DO IT.">
<meta property="og:type" content="website">
<meta property="og:title" content="Srtian&#39;Blog">
<meta property="og:url" content="http://srtian96.gitee.io/blog/page/5/index.html">
<meta property="og:site_name" content="Srtian&#39;Blog">
<meta property="og:description" content="JUST DO IT.">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Srtian&#39;Blog">
<meta name="twitter:description" content="JUST DO IT.">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://srtian96.gitee.io/blog/page/5/"/>





  <title>Srtian'Blog</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Srtian'Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">It's better to burn out than to fade away</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/blog/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/18/鹊桥仙/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/18/鹊桥仙/" itemprop="url">鹊桥仙</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-18T21:49:26+08:00">
                2018-05-18
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/随笔/" itemprop="url" rel="index">
                    <span itemprop="name">随笔</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  56
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  1
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>多情伤古，月寒影末，西风伤寒落暮。</p>
<p>银河依旧鹊桥无，但未料、离别思苦。</p>
<p>杯酒以冷，独灯摇曳，</p>
<p>人面只今何处?</p>
<p>今朝泪墨两相许，怎可得、离愁千许。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/17/Flex学习笔记/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/17/Flex学习笔记/" itemprop="url">Flex学习笔记</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-17T19:17:56+08:00">
                2018-05-17
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/CSS/" itemprop="url" rel="index">
                    <span itemprop="name">CSS</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,609
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="1-Flex布局是什么"><a href="#1-Flex布局是什么" class="headerlink" title="1.Flex布局是什么"></a>1.Flex布局是什么</h3><p>Flex是Flexble Box的缩写，意思是弹性布局，用来为盒状模型提供最大的灵活性。<br>任何一个容器都可以指定为Flex布局</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    display: flex;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>同时行内元素也可以使用Flex布局：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    display: inline-flex;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>需要注意的是在 Webkit 内核的浏览器中，必须加上-webkit前缀：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    display: -webkit-flex;  /* 主要是Safari */</span><br><span class="line">    display: flex;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>需要注意的设置为Flex布局后，子元素的float,clear和vertical-align属性将失效。</p>
<h3 id="2-基本概念"><a href="#2-基本概念" class="headerlink" title="2.基本概念"></a>2.基本概念</h3><p>采用了Flex布局的元素，称之为Flex容器，简称容器。它的所有子元素自动成为容器成员，称为Flex项目，简称项目。</p>
<p>这是一张Flex的示意图:<br><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071004.png" alt="image"></p>
<p>容器默认存在两根轴，水平的主轴（main axis）和垂直的交叉轴（cross axis）。主轴的开始位置（也就是与边框的交叉点）叫做main start，结束的位置叫做main end，交叉轴的开始位置叫做cross start，结束位置叫做cross end。</p>
<p>项目默认沿着主轴排列，单个项目占据的主轴空间叫做main szie,占据的交叉轴空间叫做cross size.</p>
<h3 id="3-容器属性"><a href="#3-容器属性" class="headerlink" title="3.容器属性"></a>3.容器属性</h3><ul>
<li>flex-direction</li>
<li>flex-wrap</li>
<li>flex-flow</li>
<li>justify-content</li>
<li>align-items</li>
<li>align-content</li>
</ul>
<h4 id="3-1-flex-direction属性"><a href="#3-1-flex-direction属性" class="headerlink" title="3.1 flex-direction属性"></a>3.1 flex-direction属性</h4><p>flex-direction属性决定主轴的方向（即项目排列的方向）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    flex-direction: row | row-reverse | column | column-reverse;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它可能会有四种值：</p>
<ul>
<li>row（默认值）：主轴为水平方向，起点在左端。</li>
<li>row-reverse：主轴为水平方向，起点在右端。</li>
<li>column：主轴为垂直方向，起点在上沿。</li>
<li>column-reverse：主轴为垂直方向，起点在下沿。<br><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071005.png" alt="image"></li>
</ul>
<h4 id="3-2-flex-wrap属性"><a href="#3-2-flex-wrap属性" class="headerlink" title="3.2 flex-wrap属性"></a>3.2 flex-wrap属性</h4><p>默认情况下，项目都是排成一条轴线上的，flex-wrap属性定义，如果一条轴线拍不下，如何换行：<br><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071006.png" alt="image"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    flex-wrap: nowrap | wrap | wrap-reverse;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它有可能以下三个值：</p>
<ul>
<li>nowrap(不换行)</li>
<li>wrap（换行。第一行在上方）</li>
<li>wrap-reverse（换行。第一行在下方）</li>
</ul>
<h4 id="3-3-flex-flow属性"><a href="#3-3-flex-flow属性" class="headerlink" title="3.3 flex-flow属性"></a>3.3 flex-flow属性</h4><p>flex-flow属性是是上面连个的简写形式。默认值是row nowrap。（毕竟上面两个属性是最常用的属性）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    flex-flow:&lt;flex-direction&gt; || &lt;flex-warp&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="3-4-justify-content属性"><a href="#3-4-justify-content属性" class="headerlink" title="3.4 justify-content属性"></a>3.4 justify-content属性</h4><p>justify-content属性定义项目在主轴的对齐方式：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box&#123;</span><br><span class="line">    justify-content: flex-start | flex-end | center | space-between | space-around;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它有可能是以下五个值，具体对齐方式与主轴的方向有关。（假设主轴是从左到右）</p>
<ul>
<li>flex-start （左对齐）</li>
<li>flex-end（右对齐）</li>
<li>center（居中）</li>
<li>space-between（两端对齐，项目之间的间隔相等）</li>
<li>space-around（每个项目两侧的间隔相等，因此，项目与项目之间的间隔比项目与边框的间隔大一倍）</li>
</ul>
<h4 id="3-5-align-items属性"><a href="#3-5-align-items属性" class="headerlink" title="3.5 align-items属性"></a>3.5 align-items属性</h4><p>align-items属性定义项目在交叉轴上是如何对齐的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box &#123;</span><br><span class="line">    align-items: flex-start | flex-end | center | baseline | stretch</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它可取5个值，具体的对齐方式与交叉轴的方式有关，默认交叉轴从上到下：</p>
<ul>
<li>flex-start：交叉轴的起点对齐</li>
<li>flex-end：交叉轴的终点对齐</li>
<li>center: 交叉轴的中点对齐</li>
<li>baseline: 项目的第一行文字的基线对齐</li>
<li>stretch（默认值）: 如果项目未设置高度或设为auto，将占满整个容器高度</li>
</ul>
<h4 id="3-6-align-content"><a href="#3-6-align-content" class="headerlink" title="3.6 align-content"></a>3.6 align-content</h4><p>align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线，该属性不起作用：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.box &#123;</span><br><span class="line">    align-content: flex-start | flex-center | center | space-between | space-around | stretch</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它可取六个值：</p>
<ul>
<li>flex-start：与交叉轴的起点对齐</li>
<li>flex-end：与交叉轴的终点对齐</li>
<li>center: 与交叉轴的中点对齐</li>
<li>space-between: 与交叉轴两端对齐，轴线之间的间隔平均分布</li>
<li>space-around：没根轴线两侧的间隔都相等。所以，轴线之间的间隔与边框之间的间隔大一倍。</li>
<li>stretch（默认值）：轴线占满整个交叉轴。<h3 id="4-项目的属性"><a href="#4-项目的属性" class="headerlink" title="4.项目的属性"></a>4.项目的属性</h3>以下6个属性可设置在项目上：</li>
<li>order</li>
<li>flex-grow</li>
<li>flex-shrink</li>
<li>flex-basis</li>
<li>flex</li>
<li>align-self</li>
</ul>
<h4 id="4-1-order属性"><a href="#4-1-order属性" class="headerlink" title="4.1 order属性"></a>4.1 order属性</h4><p>order属性定义项目的排列顺序，数值越小，排列越靠前，默认为0.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item&#123;</span><br><span class="line">    order: &lt;integer&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如图所示：</p>
<p><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071013.png" alt="image"></p>
<h4 id="4-2-flex-grow属性"><a href="#4-2-flex-grow属性" class="headerlink" title="4.2 flex-grow属性"></a>4.2 flex-grow属性</h4><p>flex-grow属性定义项目的方法比例，默认为0，即如果存在剩余空间，也不放大。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item&#123;</span><br><span class="line">    flex-grow: &lt;number&gt;; /* 默认为 0 */</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果所有项目的flex-grow属性都为1，在它们将等分剩余空间。如果一个项目的flex-grow属性为2，其他项目都为1，则前者占据的剩余空间将其他项多一倍。如图所示：<br><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071014.png" alt="image"></p>
<h4 id="4-3-flex-shrink属性"><a href="#4-3-flex-shrink属性" class="headerlink" title="4.3 flex-shrink属性"></a>4.3 flex-shrink属性</h4><p>flex-shrink属性定义了项目的缩小比例，默认为1，即如果空间不足，该项目将缩小。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item &#123;</span><br><span class="line">  flex-shrink: &lt;number&gt;; /* default 1 */</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果所有项目的flex-shrink属性都为1，当项目不足时，都将等比例缩小。如果一个项目的flex-shrink属性为0，其他项目都为1，则空间不足时，前者不缩小。负值对该属性无效。</p>
<h4 id="4-4-flex-basis"><a href="#4-4-flex-basis" class="headerlink" title="4.4 flex-basis"></a>4.4 flex-basis</h4><p>flex-basis属性定义了在分配多余空间之前，项目占据的主轴空间。浏览器根据这个属性，计算主轴是否有多余空间。它的默认值为auto，即项目的本来大小。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item&#123;</span><br><span class="line">    flex-basis: &lt;length&gt; | auto; /* 默认为auto */</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它可以设为跟width或height属性一样的值，则项目将占据固定空间。</p>
<h4 id="4-5-flex-basis"><a href="#4-5-flex-basis" class="headerlink" title="4.5 flex-basis"></a>4.5 flex-basis</h4><p>flex属性是flex-grow, flex-shrink 和 flex-basis的简写，默认值为0 1 auto。后两个属性可选。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item &#123;</span><br><span class="line">  flex: none | [ &lt;&apos;flex-grow&apos;&gt; &lt;&apos;flex-shrink&apos;&gt;? || &lt;&apos;flex-basis&apos;&gt; ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>该属性有两个快截值：auto（1 1 auto）和none（0 0 auto）。<br>建议优先使用这个属性，而不是单独写三个分离的属性，因为浏览器会推算相关值。</p>
<h4 id="4-6-align-self属性"><a href="#4-6-align-self属性" class="headerlink" title="4.6 align-self属性"></a>4.6 align-self属性</h4><p>align-self属性允许单个项目有与其他项目不一样的对齐方式，可覆盖align-liems属性，默认值auto，表示继承父元素的align-items属性，如果没有父元素，则等同于stretch。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.item &#123;</span><br><span class="line">    align-self:auto | flex-start | flex-end | center | baseline | stretch;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>该属性可能取6个值，除了auto，其他都与align-items属性完全一致。也就是上面的那几个值，如图所示：<br><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071016.png" alt="image"><br>参考资料：<br><a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html</a></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/15/那些前端应该知道的HTTP知识/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/15/那些前端应该知道的HTTP知识/" itemprop="url">那些前端应该知道的HTTP知识</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-15T23:47:07+08:00">
                2018-05-15
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/计算机网络/" itemprop="url" rel="index">
                    <span itemprop="name">计算机网络</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  4,301
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  16
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="1-HTTP简述（出自MDN）"><a href="#1-HTTP简述（出自MDN）" class="headerlink" title="1.HTTP简述（出自MDN）"></a>1.HTTP简述（出自MDN）</h3><p>超文本传输协议（HTTP）是用于传输诸如HTML的超媒体文档的应用层协议。它被设计用于Web浏览器和Web服务器之间的通信，但它也可以用于其他目的。 HTTP遵循经典的客户端-服务端模型，客户端打开一个连接以发出请求，然后等待它收到服务器端响应。 HTTP是无状态协议，意味着服务器不会在两个请求之间保留任何数据（状态）。虽然通常基于TCP / IP层，但可以在任何可靠的传输层上使用.</p>
<h3 id="2-URL和URI"><a href="#2-URL和URI" class="headerlink" title="2.URL和URI"></a>2.URL和URI</h3><p>我们经常会接触到URL（统一资源定位符），它就是我们用于访问web的一个字符串地址.而URI对我们来讲就相对比较陌生了，它的名字叫做统一资源标识符（URI）。我们来看看它们具体的区别吧：</p>
<ul>
<li>URI：uniform resource identifier 统一资源标识符，一种资源的标识，它是一种抽象的资源标识，即可以是相对的，也可以是绝对的。</li>
<li>URL：uniform resource location 统一资源定位符，同时一种资源的标识，但指明了如何定位Locate这个资源。因为它指明了定位的信息，所以必须是绝对的。</li>
</ul>
<p>（我们平时所说的相对地址，其实只是相对于另一个绝对地址而言的）</p>
<h4 id="2-1-URL"><a href="#2-1-URL" class="headerlink" title="2.1 URL"></a>2.1 URL</h4><p>URL的基本格式如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">schema://host[:port#]/path/.../[?query-string][#anchor]</span><br></pre></td></tr></table></figure></p>
<table>
<thead>
<tr>
<th>格式</th>
<th>意义 </th>
</tr>
</thead>
<tbody>
<tr>
<td>scheme</td>
<td>指定低层使用的协议(例如：http, https, ftp)。</td>
</tr>
<tr>
<td>host</td>
<td>HTTP服务器的IP地址或者域名。</td>
</tr>
<tr>
<td>port#</td>
<td>HTTP服务器的默认端口是80，这种情况下端口号可以省略。如果使用了别的端口，必须指明。</td>
</tr>
<tr>
<td>path</td>
<td>访问资源的路径。</td>
</tr>
<tr>
<td>query-string</td>
<td>发送给http服务器的数据。</td>
</tr>
<tr>
<td>anchor-</td>
<td>锚</td>
</tr>
</tbody>
</table>
<h3 id="3-HTTP报文"><a href="#3-HTTP报文" class="headerlink" title="3.HTTP报文"></a>3.HTTP报文</h3><h4 id="3-1-HTTP报文格式"><a href="#3-1-HTTP报文格式" class="headerlink" title="3.1 HTTP报文格式"></a>3.1 HTTP报文格式</h4><p>HTTP的报文格式主要分为报文首部和报文主体：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6cb586fb4?w=250&amp;h=386&amp;f=png&amp;s=6320" alt="image"></p>
<p>其中的空行用于区分报文首部和报文主体内容，是由一个回车符和一个换行符组成的。</p>
<p>无论是请求报文还是响应报文都需要有报文首部，而报文主体有些请求报文是没有的。请求报文的一般格式如下：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6303ec20c?w=477&amp;h=209&amp;f=png&amp;s=8484" alt="image"></p>
<p>而响应报文的格式是这样的：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db63206ea64?w=477&amp;h=207&amp;f=png&amp;s=9127" alt="image"></p>
<p>下面是谷歌浏览器的HTTP报文内容，其中request headers描述了请求报文头部的内容，response headers描述了响应报文头部的内容：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db635d64a71?w=1194&amp;h=589&amp;f=png&amp;s=135258" alt="image"></p>
<p>其中最常见的属性如下：</p>
<ol>
<li>URL, 即http访问的地址</li>
<li>request method, 报文的请求方式</li>
<li>status code, 状态码以及状态短语</li>
<li>Accept Encoding, 内容编码</li>
<li>Connection, 连接方式</li>
<li>Cookie, 添加的cookie内容</li>
<li>Host, 目标主机</li>
<li>User-Agent, 客户端浏览器的相关信息</li>
<li>Set-Cookie, 指定想要在Cookie中保存的内容</li>
</ol>
<p>接下来就聊一聊这些属性的作用</p>
<h4 id="3-2-HTTP请求方式（request-method）——GET-and-POST"><a href="#3-2-HTTP请求方式（request-method）——GET-and-POST" class="headerlink" title="3.2 HTTP请求方式（request method）——GET and POST"></a>3.2 HTTP请求方式（request method）——GET and POST</h4><p>发送HTTP的方式有很多，但最常用的还是POST和GET。</p>
<ul>
<li><p>GET:GET方法可以用来请求访问已经被URL识别的资源。指定的资源经过服务端解析后返回响应的内容。简单来说，就是请求的资源是文本的话，那么就保持原样返回.<br><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db645ff9125?w=579&amp;h=216&amp;f=png&amp;s=67613" alt="image"></p>
</li>
<li><p>POST:POST方法可以用来传输实体的主体。</p>
</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db62f311590?w=576&amp;h=105&amp;f=png&amp;s=30845" alt="image"></p>
<p>这两者的区别主要如下：</p>
<ol>
<li>使用目标不同<blockquote>
<p>POST与GET都用于获取信息，但是GET方式仅仅是查询，并不对服务器上的内容产生任何作用结果；每次GET的内容都是相同的。POST则常用于发送一定的内容进行某些修改操作。</p>
</blockquote>
</li>
<li>大小不同<blockquote>
<p>由于不同的浏览器对URL的长度大小有一定的字符限制，因此由于GET方式放在URL的首部中，自然也跟着首先，但是具体的大小要依浏览器而定。POST方式则是把内容放在报文内容中，因此只要报文的内容没有限制，它的大小就没有限制。</p>
</blockquote>
</li>
<li>安全性不同<blockquote>
<p>上面也说了GET是直接添加到URL后面的，直接就可以在URL中看到内容。而POST是放在报文内部的，用户无法直接看到。</p>
</blockquote>
</li>
</ol>
<p>总的来说，GET用于获取某个内容，POST用于提交某种数据请求，从使用场景来看，一般用户注册的内容是私密的，应该使用POST方式来保持私密，而当需要查询某个内容时，需要快速响应，则使用GET。</p>
<h4 id="3-3-status-code状态码"><a href="#3-3-status-code状态码" class="headerlink" title="3.3 status code状态码"></a>3.3 status code状态码</h4><p>状态码通常就是服务器端对客户端说的话，分类如下：</p>
<table>
<thead>
<tr>
<th>状态码</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>1**</td>
<td>服务器收到请求，需要请求者继续执行操作</td>
</tr>
<tr>
<td>2**</td>
<td>成功，操作被成功接收并处理</td>
</tr>
<tr>
<td>3**</td>
<td>重新定向，需要进一步的操作以完成请求</td>
</tr>
<tr>
<td>4**</td>
<td>客户端错误，请求包含语法错误或无法完成请求</td>
</tr>
<tr>
<td>5**</td>
<td>服务器错误，服务器在处理请求的过程中发生了错误</td>
</tr>
</tbody>
</table>
<p>常见的状态码：</p>
<ul>
<li>200 通常的成功 OK<blockquote>
<p>GET：请求的对应资源会作为响应返回。响应将包含描述或操作的结果。<br>POST：返回处理对应请求的结果。</p>
</blockquote>
</li>
<li>204 成功处理请求，没有返回任何内容 No Content<blockquote>
<p>表示服务器接收到的请求已经处理完毕，但是服务器不需要返回响应。比如，客户端是浏览器的话，那么浏览器显示的页面不会发生更新。</p>
</blockquote>
</li>
<li>206 Partial Content<blockquote>
<p>成功处理了部分GET请求</p>
</blockquote>
</li>
<li>301 Moved Permanently<blockquote>
<p>请求的网页已永久移动到新位置，永久性重定向</p>
</blockquote>
</li>
<li>302 Found<blockquote>
<p>网站临时性重定向，暂时不能访问（备案、被查）</p>
</blockquote>
</li>
<li>303 See Other<blockquote>
<p>该状态码表示由于请求对应的资源存在另一个URI，并指定必须使用GET方法定向获取请求的资源。和302不同的是，302是不会改变上次的请求方法</p>
</blockquote>
</li>
<li>304 Not Modified<blockquote>
<p>访问不了，并返回和上次一样的话,表示资源未被修改过，还是和上次访问时一样。</p>
</blockquote>
</li>
<li>307 Temporary Redirect<blockquote>
<p>临时重定向，和302、303类似，不同的是，不会指定客户端要用什么样的方法请求，</p>
</blockquote>
</li>
<li>400 Bad Request<blockquote>
<p>表示客户端中存在语法错误，导致服务器无法理解该请求。客户端需要修改请求的内容后再次发送请求。</p>
</blockquote>
</li>
<li>401 Unauthorized<blockquote>
<p>即用户没有必要的凭据。该状态码表示当前请求需要用户验证。</p>
</blockquote>
</li>
<li>403 Forbidden<blockquote>
<p>服务器已经理解请求，但是拒绝执行它。</p>
</blockquote>
</li>
<li>404 Not Found<blockquote>
<p>服务器找不到请求的网页。</p>
</blockquote>
</li>
<li>500 Internal Server Error<blockquote>
<p>服务器遇到错误，无法完成请求。</p>
</blockquote>
</li>
<li>503 Service Unavailable<blockquote>
<p>由于临时的服务器维护或者过载，服务器当前无法处理请求。这个状况是暂时的.</p>
</blockquote>
</li>
</ul>
<h4 id="3-4-内容编码-Accept-Encoding"><a href="#3-4-内容编码-Accept-Encoding" class="headerlink" title="3.4 内容编码 Accept Encoding"></a>3.4 内容编码 Accept Encoding</h4><p>由于有些报文的内容会过大，为了减少传输时间，HTTP会采取一些压缩的措施，例如上面的报文信息中，Accept-Encoding 就定义了内容编码的格式gzip。</p>
<p>总的来说内容编码的格式有以下几种：</p>
<ul>
<li>gzip:GNU压缩格式</li>
<li>compress:UNIX系统的标准压缩格式</li>
<li>deflate:是一种同时使用了LZ77和哈夫曼编码的无损失压缩格式</li>
<li>identity:不进行压缩</li>
</ul>
<h4 id="3-5-持久化——connection"><a href="#3-5-持久化——connection" class="headerlink" title="3.5 持久化——connection"></a>3.5 持久化——connection</h4><p>正常发送HTTP时，我们需要建立TCP的连接，然后再发送报文：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db63377aff6?w=599&amp;h=259&amp;f=png&amp;s=24567" alt="image"></p>
<p>如果每次都要发送HTTP报文都需要经历上面的过程，这无疑将会耗费很多时间在建立和断开连接的过程中。因此HTTP使用了 connection 属性，用于指定连接的方式。当设置成keep-alive时，就会建立一条持久化的连接。这样就不需要每次都建立连接，再中断连接：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6cfd402bf?w=578&amp;h=243&amp;f=png&amp;s=28374" alt="image"></p>
<p>（HTTP1.1中connection默认开启keep-alive）</p>
<h4 id="3-6-无状态的HTTP——cookie"><a href="#3-6-无状态的HTTP——cookie" class="headerlink" title="3.6 无状态的HTTP——cookie"></a>3.6 无状态的HTTP——cookie</h4><p>由于HTTP是一种无状态的协议，这是由于Web服务器要面对很多浏览器的并发访问，为了提高Web服务器对并发访问的处理能力，在设计HTTP协议时规定，Web服务器发送HTTP应答报文和文档时，不保存发出请求的Web浏览器进程的任何状态信息，从而减轻服务器端的负载，同时无状态也减小了HTTP请求的开销。</p>
<p>但当有些场景需要时刻记住用户的信息时，无状态很明显不能满足需求，因此HTTP提供了cookie来解决这个问题，cookie技术通过在请求和相应报文中写入cookie信息来控制客户端的状态。cookie会根据从服务端发送的相应报文内的一个叫做set-cookie的首部字段信息，通知客户端保存cookie。当下次客户端再往服务器发送请求的时候，客户端会自动在请求头加入cookie值后发送出去。在没有cookie状态下的请求：<br><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6d2077346?w=637&amp;h=270&amp;f=png&amp;s=59536" alt="image"></p>
<p>当存有cookie后的请求：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6e9f972fc?w=601&amp;h=243&amp;f=png&amp;s=51496" alt="image"></p>
<p>简单来说Cookie是一种由服务器端确定，并保存在客户端浏览器中的内容。这样每次当我们去添加用户的信息，请求会自动添加cookie中对应的内容。</p>
<p>（关于浏览器端的数据存储感兴趣的可以看下这篇文章：<a href="https://juejin.im/post/5aeaf545518825673b61ddc8" target="_blank" rel="noopener">：聊一聊常见的浏览器端数据存储方案</a>）</p>
<h3 id="3-7-范围请求"><a href="#3-7-范围请求" class="headerlink" title="3.7 范围请求"></a>3.7 范围请求</h3><p>在一些场景下，我们在使用HTTP报文请求一些很大的图片时，加载过程往往会很慢（比如一些摄影网站）。这时候我们就会发现一些图片是一块一块加载的，这是因为设置了HTTP请求的长度，从而分块的加载资源。在请求报文中使用Range属性，在响应报文中使用Content-Type属性都可以进行指定一定自己的HTTP请求。</p>
<h4 id="3-8-报文首部总结"><a href="#3-8-报文首部总结" class="headerlink" title="3.8 报文首部总结"></a>3.8 报文首部总结</h4><p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6e0576c28?w=705&amp;h=1344&amp;f=jpeg&amp;s=151236" alt="image"></p>
<p>（图转自：<a href="http://www.cnblogs.com/xing901022/p/4311987.html）" target="_blank" rel="noopener">http://www.cnblogs.com/xing901022/p/4311987.html）</a></p>
<h3 id="4-HTTP方法"><a href="#4-HTTP方法" class="headerlink" title="4.HTTP方法"></a>4.HTTP方法</h3><p>HTTP 支持几种不同的请求命令，这些命令被称为 HTTP 方法（HTTP method）。每<br>条 HTTP 请求报文都包含一个方法。这个方法会告诉服务器要执行什么动作（获取<br>一个 Web 页面、运行一个网关程序、删除一个文件等）。下表是一些常见的HTTP方法：</p>
<table>
<thead>
<tr>
<th>HTTP方法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>GET</td>
<td>从服务器向客户端发送命名资源</td>
</tr>
<tr>
<td>PUT</td>
<td>将来自客户端的数据存储到一个命名的服务器资源中去</td>
</tr>
<tr>
<td>DELETE</td>
<td>从服务器中删除命名资源</td>
</tr>
<tr>
<td>POST</td>
<td>将客户端数据发送到一个服务器网关应用程序</td>
</tr>
<tr>
<td>HEAD</td>
<td>仅发送命名资源响应中的 HTTP 首部</td>
</tr>
</tbody>
</table>
<p>（GET和POST已在上面讨论过了，这里就不在讨论了）</p>
<h4 id="4-1、PUT传输文件"><a href="#4-1、PUT传输文件" class="headerlink" title="4.1、PUT传输文件"></a>4.1、PUT传输文件</h4><p>PUT方法用于传输文件，就像FTP协议的文件上传一样，要求在请求报文的主题中包含文件内容，然后保存到请求URI指定的位置。由于PUT方法不带验证机制，任何人都可以上传文件，所以存在安全性问题，因此一般的web网站不使用该方法。</p>
<h4 id="4-2、DELETE删除文件"><a href="#4-2、DELETE删除文件" class="headerlink" title="4.2、DELETE删除文件"></a>4.2、DELETE删除文件</h4><p>DELETE方法用来删除文件，是与put相反的方法，DELETE方法按照请求url删除指定的资源。其本质和PUT方法一样不带验证机制，所以建议少用DELETE方法。</p>
<h4 id="4-3、HEAD获取报文首部"><a href="#4-3、HEAD获取报文首部" class="headerlink" title="4.3、HEAD获取报文首部"></a>4.3、HEAD获取报文首部</h4><p>HEAD和GET方法一样，只是不返回报文主体部分，通常用于确认url的有效性及资源更新的日期时间等。<br><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6f24e7667?w=588&amp;h=91&amp;f=png&amp;s=24582" alt="image"></p>
<h3 id="5-HTTPS"><a href="#5-HTTPS" class="headerlink" title="5.HTTPS"></a>5.HTTPS</h3><h4 id="5-1-什么是HTTPS"><a href="#5-1-什么是HTTPS" class="headerlink" title="5.1 什么是HTTPS"></a>5.1 什么是HTTPS</h4><p>HTTPS（全称：Hyper Text Transfer Protocol over Secure Socket Layer），是以安全为目标的HTTP通道，简单来说就是是HTTP的安全版本，即在HTTP下加入SSL层，HTTPS的安全基石是SSL，因此加密的详细内容就需要SSL。 它现在已经被广泛应用，比如GitHub，支付宝，掘金等。</p>
<h4 id="5-2-为什么需要HTTPS"><a href="#5-2-为什么需要HTTPS" class="headerlink" title="5.2 为什么需要HTTPS"></a>5.2 为什么需要HTTPS</h4><p>这是由于HTTP有这么几个缺点：</p>
<ul>
<li>传输的时候使用明文传输，这显然会可以伪造一些HTTP访问，这显然会造成一些困扰。比如Jmeter就是典型的例子，伪造一大堆的HTTP URL然后压力测试，这也就是DOS攻击的一种。</li>
<li>无法验证报文的完整性，比如一个HTTP的报文已经被不法者截取并且篡改，服务器端也无法验证。<h4 id="5-3-HTTP-与-HTTPS-的区别"><a href="#5-3-HTTP-与-HTTPS-的区别" class="headerlink" title="5.3 HTTP 与 HTTPS 的区别"></a>5.3 HTTP 与 HTTPS 的区别</h4>正是由于以上这些缺点，HTTPS作出了以下一些改变：</li>
<li>HTTP 是明文传输，HTTPS 通过 SSL\TLS 进行了加密</li>
<li>HTTP 的端口号是 80，HTTPS 是 443</li>
<li>HTTPS 需要到 CA 申请证书，一般免费证书很少，需要交费</li>
<li>-HTTPS 的连接很简单，是无状态的；HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议，比 HTTP 协议安全。<h4 id="5-4-HTTPS的缺陷"><a href="#5-4-HTTPS的缺陷" class="headerlink" title="5.4 HTTPS的缺陷"></a>5.4 HTTPS的缺陷</h4>可以说HTTPS相对于HTTP就是套上了黄金甲的圣斗士，变了身的奥特曼，沉睡了的毛利小五郎，不仅仅提升了安全，还提升了逼格。但HTTPS也有一些缺陷：</li>
<li>通信的速度变慢，由于需要加密，一个握手就多了好几个往返</li>
<li>对用户的机器负载的增加。（说出来你们可能不信，我们学校一到晚上，用HTTPS协议的网站基本上都上不了）<br><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db6fdc9f9e2?w=640&amp;h=485&amp;f=jpeg&amp;s=37246" alt="image"><h3 id="6-HTTP认证"><a href="#6-HTTP认证" class="headerlink" title="6. HTTP认证"></a>6. HTTP认证</h3>有一些网站需要用户的登录从而获取用户的个人信息来进行接下来的操作，因此需要随时知道这些消息，但是肯定不能每次都让用户输入用户密码，这样会让用户感觉很不爽，因此HTTP也自带了认证的功能，认证方式主要如下：</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db708db432c?w=395&amp;h=228&amp;f=jpeg&amp;s=13767" alt="image"></p>
<h4 id="6-1-BASIC认证"><a href="#6-1-BASIC认证" class="headerlink" title="6.1 BASIC认证"></a>6.1 BASIC认证</h4><p>其中BASIC认证是最简单的认证，大致过程如下：</p>
<ol>
<li>客户端访问某URL。</li>
<li>服务器端返回401状态码，提示用户输入用户名密码。</li>
<li>用户输入用户名密码，通过BASE64编码传输。</li>
<li>服务器通过认证，返回状态码200</li>
</ol>
<p>但它有以下缺陷：</p>
<ol>
<li>仅仅通过BASE64编码，其实还是属于明文传输，安全性不高</li>
<li>有的浏览器不支持注销<h4 id="6-2-DIGEST认证"><a href="#6-2-DIGEST认证" class="headerlink" title="6.2 DIGEST认证"></a>6.2 DIGEST认证</h4>正是由于BASIC认证存在弱点，因此从HTTP/1.1起就有了DIGEST认证，DIGEST认证同样使用质询/响应的方式，但不会像BASIC认证那样直接明文发送密码。<br><img src="https://user-gold-cdn.xitu.io/2018/5/15/16363db70a13a202?w=690&amp;h=496&amp;f=jpeg&amp;s=75157" alt="image"><h4 id="6-3-SSL认证（比较常见）"><a href="#6-3-SSL认证（比较常见）" class="headerlink" title="6.3 SSL认证（比较常见）"></a>6.3 SSL认证（比较常见）</h4>SSL客户端认证是借由HTTPS的客户端证书完成认证的方式。凭借客户端证书认证，服务器可确认访问是否来自已登录的客户端。</li>
</ol>
<p>SSL客户端认证的步骤：</p>
<ol>
<li>服务器接收到需要认证资源的请求时，服务器会发送CertificateRequest报文，要求客户端提供客户端证书。</li>
<li>客户端将客户端证书信息以Client Certificate报文方式发送给服务器。</li>
<li>服务器验证客户端证书验证通过后才能领取证书内客户端的公开密钥，然后开始HTTPS加密通信。</li>
</ol>
<p>像支付宝，网银之类对安全要求很高的网站，在登录时，都需要下载一个数字认证，这个数字认证就属于一种SSL客户端的验证。但它的缺点也很明显，需要手动下载，对于现在越来越懒的网民们来讲会感觉很麻烦（包括我）</p>
<h4 id="6-4-表单认证（最常用的）"><a href="#6-4-表单认证（最常用的）" class="headerlink" title="6.4 表单认证（最常用的）"></a>6.4 表单认证（最常用的）</h4><p>最后一种认证方式是最常见的，我们可以通过cookie或session来进行认证。</p>
<h5 id="Session管理和Cookie应用的结合"><a href="#Session管理和Cookie应用的结合" class="headerlink" title="Session管理和Cookie应用的结合"></a>Session管理和Cookie应用的结合</h5><p>我前面提到过，HTTP是无状态协议，无法实现状态管理，因此有了cookie。我们就可以使用Cookie来管理Session（会话），以弥补HTTP协议中不存在的状态管理功能。</p>
<p>认证步骤：</p>
<ol>
<li>客户端把用户的ID和密码等相关信息放入报文的实体部分，然后通常以POST请求的方式发送给服务器。</li>
<li>服务器会发放用以识别用户的Session ID。通过验证从客户端发送过来的登录信息进行身份认证，将用户的认证状态和Session ID绑定后记录在服务器端。</li>
<li>客户端接收到Session ID后，会将其作为Cookie保存在本地。下次向服务器发送请求时，浏览器自动发送Cookie，Session ID会随之发送到服务器。服务端通过验证接收到的Session ID识别用户和其认证状态，然后用户就能执行特定的操作了。</li>
</ol>
<p>参考资料：</p>
<ol>
<li>《图解HTTP》</li>
<li><a href="https://juejin.im/post/59e4c02151882578d02f4aca" target="_blank" rel="noopener">https://juejin.im/post/59e4c02151882578d02f4aca</a></li>
<li><a href="http://www.cnblogs.com/xing901022/p/4309840.html" target="_blank" rel="noopener">http://www.cnblogs.com/xing901022/p/4309840.html</a></li>
</ol>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/11/从HTML5 WebSocket到Socket.io/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/11/从HTML5 WebSocket到Socket.io/" itemprop="url">从HTML5 WebSocket到Socket.io</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-11T18:29:36+08:00">
                2018-05-11
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/计算机网络/" itemprop="url" rel="index">
                    <span itemprop="name">计算机网络</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  3,141
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  12
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="HTML5-WebSocket概述"><a href="#HTML5-WebSocket概述" class="headerlink" title="HTML5 WebSocket概述"></a>HTML5 WebSocket概述</h2><p>作为新一代的web标准，HTML5为我们提供了很多有用的东西，比如canvas，本地存储（已经分离出去了），多媒体编程接口，当然还有我们的WebSocket。WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯（full-duplex）的网络技术，可以传输基于信息的文本和二进制的数据。它于2011年被IETF定为标准 RFC 6455，同时WebSocket API也被W3C定为标准。</p>
<h2 id="一、WebSocket产生的背景"><a href="#一、WebSocket产生的背景" class="headerlink" title="一、WebSocket产生的背景"></a>一、WebSocket产生的背景</h2><h3 id="1-黎明前的黑暗——实时web应用的需求"><a href="#1-黎明前的黑暗——实时web应用的需求" class="headerlink" title="1.黎明前的黑暗——实时web应用的需求"></a>1.黎明前的黑暗——实时web应用的需求</h3><p>web应用的信息交互过程我想大家或多或少都知道一些，通常是客户端通过浏览器发出一个请求，然后服务器端在接受和审核请求后,进行处理并将结果返回给客户端，最后由客户端的浏览器将信息呈现出来。这种通信机制在信息交互不是特别频繁的情况下并没有太大的问题，但对于那些实时性要求高、海量数据并发的应用来说，就显得捉襟见肘了，比如现在常见的网页游戏，证券网站，RSS订阅推送，网页实时对话，打车软件等。通常当客户端准备呈现一些信息时，这些信息在服务器端很有可能就已经过时了。为了满足以上那些场景，大佬们研究出来了一些折衷方案，其中最常用的就是普通轮询和Comet技术，而Comet技术实际上就是轮询的改进，细分起来Comet有两种实现方式：</p>
<ul>
<li>长轮询机制</li>
<li>流技术机制</li>
</ul>
<h4 id="1-1-长轮询机制"><a href="#1-1-长轮询机制" class="headerlink" title="1.1 长轮询机制"></a>1.1 长轮询机制</h4><p>长轮序是对普通轮询的改进和提高。普通轮询简单来说，就是客户端每隔一定的时间就向服务器端发送请求，从而以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是，客户端已固定的频率发送请求时，很可能服务端的数据没有更新，产生很多无用的网络传输，非常低效。</p>
<p>为了减少无效的网络传输，长轮询对普通轮询进行了改进和提高，当服务器端没有数据更新时，链接会保持一段时间的周期，直到数据或状态发生改变或连接时间过期，通过这种机制我们就可以减少很多无效的客户端和服务器间的交互。当然，如果服务器端的数据变更非常频繁的话，这种机制并没有有效的提高性能，和普通轮询没有太大的区别，且长轮询也会耗费更多的资源，比如CPU,内存,带宽等。</p>
<h4 id="1-2-流技术机制"><a href="#1-2-流技术机制" class="headerlink" title="1.2 流技术机制"></a>1.2 流技术机制</h4><p>流技术机制简单来说就是客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器接到请求后作出回应，并不断更新状态，以保证客户端和服务器端的连接不过期。通过这种机制就可以将服务器端的信息不断传向客户端，从而保证信息的时效性。但这种机制对于用户体验并不友好，需要针对不同的浏览器升级不同的方案来改进用户体验，同时这种机制如果在并发情况下发生时，会对服务器的资源造成很大压力。</p>
<h3 id="2-黎明的到来——WebSocket"><a href="#2-黎明的到来——WebSocket" class="headerlink" title="2.黎明的到来——WebSocket"></a>2.黎明的到来——WebSocket</h3><p>正是出于以上几种解决方案都有着各自的局限性,HTML5 WebSocket也就应运而生了，浏览器可以通过JavaScript借助现有的HTTP协议来向服务器发出WebSocket连接的请求，当连接建立后，客户端和服务器端就可以直接通过TCP连接来直接进行数据交换。这是由于websocket协议本质上就是一个TCP连接，所以在数据传输的稳定性和传输量上有所保证，且相对于以往的轮询和Comet技术在性能方面也有了长足的进步：<br><img src="https://user-gold-cdn.xitu.io/2018/5/11/1634ea2e300a0c6f?w=504&amp;h=360&amp;f=jpeg&amp;s=17994" alt="image"></p>
<p>有一点需要注意的是虽然websocket在通信时需要借助HTTP，但它本质上和HTTP有着很大的区别：</p>
<ul>
<li>WebSocket是一种双向通信协议，在建立连接之后，WebSocket服务端和客户端都能主动向对方发送或者接受数据。</li>
<li>WebSocket需要先连接，只有再连接后才能进行相互通信。</li>
</ul>
<p>他们的关系其实就和这张图表现的一样，虽然有相交的部分，但依然有着很大的区别：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/11/1634ea2e0ed4878d?w=374&amp;h=133&amp;f=jpeg&amp;s=9144" alt="image"></p>
<h2 id="二、WebSocket-API的用法"><a href="#二、WebSocket-API的用法" class="headerlink" title="二、WebSocket API的用法"></a>二、WebSocket API的用法</h2><p>由于每个服务器端的语言都有着自己的API，因此首先我们来讨论客户端的API：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个socket实例：</span></span><br><span class="line"><span class="keyword">const</span> socket = <span class="keyword">new</span> WebSocket(ws:<span class="comment">//localhost:9093')</span></span><br><span class="line"><span class="comment">// 打开socket</span></span><br><span class="line">socket.onopen = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 发送一个初始化消息</span></span><br><span class="line">  	socket.send(<span class="string">'Hello Server!'</span>)</span><br><span class="line">  	 <span class="comment">// 服务器有响应数据触发</span></span><br><span class="line">    socket.onmessage = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123; </span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'Client received a message'</span>, event)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 出错时触发，并且会关闭连接。这时可以根据错误信息进行按需处理</span></span><br><span class="line">    socket.onerror = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  	    <span class="built_in">console</span>.log(<span class="string">'error'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 监听Socket的关闭</span></span><br><span class="line">    socket.onclose = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123; </span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'Client notified socket has closed'</span>, event)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 关闭Socket</span></span><br><span class="line">    socket.close(<span class="number">1000</span>, <span class="string">'closing normally'</span>) </span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>是不是感觉HTML5 websocket所提供的API贼鸡儿简单，没错，就是这么简单。但有几点我们需要注意：</p>
<ul>
<li>在创建socket实例的时候，new WebSocket()接受两个参数，第一个参数是ws或wss,第二个参数可以选填自定义协议，如果是多协议，可以是数组的方式。</li>
<li>WebSocket中的send方法不是任何数据都能发送的，现在只能发送三类数据，包括UTF-8的string类型（会默认转化为USVString），ArrayBuffer和Blob，且只有在建立连接后才能使用。（感谢大佬指出错误，已修改）</li>
<li>在使用socket.close(code,[reason])关闭连接时，code和reason都是选填的。code是一个数字值表示关闭连接的状态号，表示连接被关闭的原因。如果这个参数没有被指定，默认的取值是1000 （表示正常连接关闭）,而reason是一个可读的字符串，表示连接被关闭的原因。这个字符串必须是不长于123字节的UTF-8 文本。</li>
</ul>
<h3 id="1-ws和wss"><a href="#1-ws和wss" class="headerlink" title="1.ws和wss"></a>1.ws和wss</h3><p>我们在上面提到过，创建一个 socket 实例时可以选填ws和wss来进行通信协议的确定。他们两个其实很像HTTP和HTTPS之间的关系。其中ws表示纯文本通信，而wss表示使用加密信道通信（TCP+TLS）。那为啥不直接使用HTTP而要自定义通信协议呢？这就要从WebSocket的目的说起来，WebSocket 的主要功能就是为了给浏览器中的应用与服务器端提供优化的，双向的通信机制，但这不代表 WebScoket 只能局限于此，它当然还能够用于其他的场景，这就需要他可以通过非HTTP协议来进行数据交换，因此 WebSocket 也就采用了自定义 URI 模式，以确保就算没有HTTP，也能进行数据交换。</p>
<p>ws和wss：</p>
<ul>
<li><strong>ws协议</strong>：普通请求，占用与HTTP相同的80端口</li>
<li><strong>wss协议</strong>：基于SSL的安全传输，占用与TLS相同的443端口。</li>
</ul>
<p>注：有些HTTP中间设备有时候可能会不理解WebSocket，而导致各种诸如：盲目连接升级，乱修改内容等问题。而WSS就很好的解决了这个问题，它建立了一个端到端的安全通道，这个通道对中间设备模糊了数据，因此中间设备就不能感知到数据，也就无法对请求做一些特殊处理了。</p>
<h2 id="三、WebSocket协议的规范"><a href="#三、WebSocket协议的规范" class="headerlink" title="三、WebSocket协议的规范"></a>三、WebSocket协议的规范</h2><p>以下是一个典型的WebSocket发起请求到响应请求的示例：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">客户端到服务端：</span><br><span class="line">GET / HTTP/1.1</span><br><span class="line">Connection:Upgrade</span><br><span class="line">Host:127.0.0.1:8088</span><br><span class="line">Origin:null</span><br><span class="line">Sec-WebSocket-Extensions:x-webkit-deflate-frame</span><br><span class="line">Sec-WebSocket-Key:puVOuWb7rel6z2AVZBKnfw==</span><br><span class="line">Sec-WebSocket-Version:13</span><br><span class="line">Upgrade:websocket</span><br><span class="line"></span><br><span class="line">服务端到客户端：</span><br><span class="line">HTTP/1.1 101 Switching Protocols</span><br><span class="line">Connection:Upgrade</span><br><span class="line">Server:beetle websocket server</span><br><span class="line">Upgrade:WebSocket</span><br><span class="line">date: Thu, 10 May 2018 07:32:25 GMT</span><br><span class="line">Access-Control-Allow-Credentials:true</span><br><span class="line">Access-Control-Allow-Headers:content-type</span><br><span class="line">Sec-WebSocket-Accept:FCKgUr8c7OsDsLFeJTWrJw6WO8Q=</span><br></pre></td></tr></table></figure></p>
<p>我们可以看到，WebSocket协议和HTTP协议乍看并没有太大的区别，但细看下来，区别还是有些的，这其实是一个握手的http请求，首先请求和响应的，”Upgrade:WebSocket”表示请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket协议。从客户端到服务器端请求的信息里包含有”Sec-WebSocket-Extensions”、“Sec-WebSocket-Key”这样的头信息。这是客户端浏览器需要向服务器端提供的握手信息，服务器端解析这些头信息，并在握手的过程中依据这些信息生成一个28位的安全密钥并返回给客户端，以表明服务器端获取了客户端的请求，同意创建 WebSocket 连接。</p>
<p>当握手成功后，这个时候TCP连接就已经建立了，客户端与服务端就能够直接通过WebSocket直接进行数据传递。不过服务端还需要判断一次数据请求是什么时候开始的和什么时候是请求的结束的。在WebSocket中，由于浏览端和服务端已经打好招呼，如我发送的内容为utf-8 编码，如果我发送0x00,表示包的开始，如果发送了0xFF，就表示包的结束了。这就解决了黏包的问题。</p>
<h2 id="四、兼容性情况"><a href="#四、兼容性情况" class="headerlink" title="四、兼容性情况"></a>四、兼容性情况</h2><pre><code>浏览器                     支持情况
Chrome                Supported in version 4+
Firefox                Supported in version 4+
Internet Explorer    Supported in version 10+
Opera                Supported in version 10+
Safari                Supported in version 5+
</code></pre><h2 id="五、Socket-IO"><a href="#五、Socket-IO" class="headerlink" title="五、Socket.IO"></a>五、Socket.IO</h2><p>简单来说Socket.IO就是对WebSocket的封装，并且实现了WebSocket的服务端代码。Socket.IO将WebSocket和轮询（Polling）机制以及其它的实时通信方式封装成了通用的接口，并且在服务端实现了这些实时机制的相应代码。也就是说，WebSocket仅仅是Socket.IO实现实时通信的一个子集。Socket.IO简化了WebSocket API，统一了返回传输的API。传输种类包括：</p>
<ul>
<li>WebSocket</li>
<li>Flash Socket</li>
<li>AJAX long-polling</li>
<li>AJAX multipart streaming</li>
<li>IFrame</li>
<li>JSONP polling。</li>
</ul>
<p>上面的排序基本上是根据传输的速度进行排序的，socket.io会根据浏览器的兼容情况对这些传输种类进行选择。</p>
<p>我们来看一下服务端的Socket.IO基本API：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 引入socke.io</span></span><br><span class="line"><span class="keyword">const</span> io = <span class="built_in">require</span>(<span class="string">'socket.io'</span>)(<span class="number">80</span>)</span><br><span class="line"><span class="comment">// 监听客户端连接,回调函数会传递本次连接的socket</span></span><br><span class="line">io.on(<span class="string">'connection'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">socket</span>))</span></span><br><span class="line"><span class="function">// 给所有客户端广播消息</span></span><br><span class="line"><span class="function"><span class="title">io</span>.<span class="title">sockets</span>.<span class="title">emit</span>(<span class="params"><span class="string">'String'</span>,data</span>)</span></span><br><span class="line"><span class="function">// 给指定的客户端发送消息</span></span><br><span class="line"><span class="function"><span class="title">io</span>.<span class="title">sockets</span>.<span class="title">socket</span>(<span class="params">socketid</span>).<span class="title">emit</span>(<span class="params"><span class="string">'String'</span>, data</span>)</span></span><br><span class="line"><span class="function">// 监听客户端发送的信息</span></span><br><span class="line"><span class="function"><span class="title">socket</span>.<span class="title">on</span>(<span class="params"><span class="string">'String'</span>,function(data</span>))</span></span><br><span class="line"><span class="function">// 给该<span class="title">socket</span>的客户端发送消息</span></span><br><span class="line"><span class="function"><span class="title">socket</span>.<span class="title">emit</span>(<span class="params"><span class="string">'String'</span>, data</span>)</span></span><br></pre></td></tr></table></figure></p>
<p>另外，Socket.IO还提供了一个Node.JS API，它看起来很像客户端API。所以我们来看看它的实际应用吧：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// socket-server.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 需要使用HTTP模块来启动服务器和Socket.IO</span></span><br><span class="line"><span class="keyword">const</span> http= <span class="built_in">require</span>(<span class="string">'http'</span>), </span><br><span class="line"><span class="keyword">const</span> io= <span class="built_in">require</span>(<span class="string">'socket.io'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server= http.createServer(<span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>)</span>&#123; </span><br><span class="line">    <span class="comment">// 发送HTML的headers和message</span></span><br><span class="line">    res.writeHead(<span class="number">200</span>,&#123; <span class="string">'Content-Type'</span>: <span class="string">'text/html'</span> &#125;)</span><br><span class="line">    res.end(<span class="string">'&lt;p&gt;Hello Socket.IO!&lt;p&gt;'</span>)</span><br><span class="line">&#125;); </span><br><span class="line"><span class="comment">// 在8080端口启动服务器</span></span><br><span class="line">server.listen(<span class="number">8080</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个Socket.IO实例，并把它传递给服务器</span></span><br><span class="line"><span class="keyword">const</span> socket= io.listen(server)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个连接监听器</span></span><br><span class="line">socket.on(<span class="string">'connection'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">client</span>) </span>&#123; </span><br><span class="line"></span><br><span class="line"><span class="comment">// 连接成功，开始监听</span></span><br><span class="line">client.on(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>&#123; </span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Received message from client!'</span>,event)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 连接失败</span></span><br><span class="line">client.on(<span class="string">'disconnect'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    clearInterval(interval)</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Server has disconnected'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>然后我们就可以启动这个文件了：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node socket-server.js</span><br></pre></td></tr></table></figure>
<p>然后我们就可以创建一个每秒钟发送消息到客户端的发送器了；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> interval= setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123; </span><br><span class="line">  client.send(<span class="string">'This is a message from the server,hello world'</span> + <span class="keyword">new</span> <span class="built_in">Date</span>().getTime())</span><br><span class="line">&#125;,<span class="number">1000</span>)</span><br></pre></td></tr></table></figure>
<hr>
<p>注：需要注意的是，如果我们想在前端使用socket.IO,我们需要下载这个：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install socket.io-client --save</span><br></pre></td></tr></table></figure>
<p>然后再连接网络：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> io <span class="keyword">from</span> <span class="string">'socket.io-client'</span></span><br><span class="line"><span class="keyword">const</span> socket = io(<span class="string">'ws://localhost:8080'</span>)</span><br></pre></td></tr></table></figure>
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/聊一聊常见的浏览器端数据存储方案/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/聊一聊常见的浏览器端数据存储方案/" itemprop="url">聊一聊常见的浏览器端数据存储方案</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:46:54+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/浏览器/" itemprop="url" rel="index">
                    <span itemprop="name">浏览器</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  3,553
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  14
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言："><a href="#前言：" class="headerlink" title="前言："></a>前言：</h3><p>五一假期在撸代码的时候用到cookie，感觉对浏览器的数据存储方案不是很了解，因此又去翻了两本大头书中间的关于浏览器端数据存储的章节，同时去MDN逛了逛，又看了几篇文章，算是对浏览器的数据存储方案有了一个了解，在此总结一下！</p>
<h2 id="浏览器存储"><a href="#浏览器存储" class="headerlink" title="浏览器存储"></a>浏览器存储</h2><p>在浏览器端存储数据对我们是很有用，这相当于赋予浏览器记忆的功能，可以纪录用户的所有状态信息，增强用户体验。比如当纪录用户的登陆状态时，可以让用户能够更快的进行访问，而不是每次登陆时都需要去进行繁琐的操作。</p>
<p>总的来说,现在市面上最常见的数据存储方案是以下三种：</p>
<ul>
<li>Cookie</li>
<li>web存储 (locaStorage和seesionStorage)</li>
<li>IndexedDB</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/5/3/16325ce01e799f9f?w=749&amp;h=356&amp;f=png&amp;s=111928" alt="图片.png"></p>
<h2 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h2><p>Cookie的又称是HTTP Cookie，最初是在客户端用于存储会话信息，从底层来看，它作为HTTP协议的一种扩展实现，Cookie数据会自动在web浏览器和web服务器之间传输，因此在服务器端脚本就可以读写存储的cookie的值。Cookie通常用于存储一些通用的数据，比如用户的登陆状态，首选项等。虽然随着时代的进步，HTML5所提供的web存储机制已经逐步替代了Cookie，但有些较为老的浏览器还是不兼容web存储机制，此外在用户认证方面，cookie+session还是当今的主流，也非常好用。因此正处于这个老旧更替阶段的我们对于它还是要学习学习的。</p>
<h3 id="Cookie的优点"><a href="#Cookie的优点" class="headerlink" title="Cookie的优点"></a>Cookie的优点</h3><p>首先由于操作Cookie的API很早就已经定义和实现了，因此相比于其他的数据存储方式，Cookie的兼容性非常的好，兼容现在市面上所有的主流浏览器，我们在使用它的时候完全不用担心兼容问题。</p>
<h3 id="Cookie的缺点"><a href="#Cookie的缺点" class="headerlink" title="Cookie的缺点"></a>Cookie的缺点</h3><p>说到Cookie的缺点，那就有点多了，不然也不会在Cookie后面出现web存储等新的数据存储的方案了。<br>总结起来Cookie的缺点主要是以下几点：</p>
<ol>
<li>存储量小。虽不同浏览器的存储量不同，但基本上都是在4kb左右。</li>
<li>影响性能。由于Cookie会由浏览器作为请求头发送，因此当Cookie存储信息过多时，会影响特定域的资源获取的效率，增加文档传输的负载。</li>
<li>只能储存字符串。</li>
<li>安全问题。存储在Cookie的任何数据可以被他人访问，因此不能在Cookie中储存重要的信息。</li>
<li>由于第三方Cookie的滥用，所以很多老司机在浏览网页时会禁用Cookie，所以我们不得不测试用户是否支持Cookie，这也是很麻烦的一件事。<h3 id="Cookie的操作"><a href="#Cookie的操作" class="headerlink" title="Cookie的操作"></a>Cookie的操作</h3>基本的Cookie操作主要有三个：读取，写入和删除。但在 JavaScript 中去处理 cookie 是一件很繁琐的事情，因为 cookie 中的所有的名字和值都是经过URI编码的，所以当我们必须使用 decodeURICompoent 来进行解码才能得到 cookie 的值。我们来看看 CookieUtil 对象是如何操纵 cookie 的：</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> CookieUtil = &#123;</span><br><span class="line">	<span class="comment">// get可根据cookie的名字获取相应的值</span></span><br><span class="line">	get: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">		<span class="keyword">const</span> cookieName = encodeURIcOMPONET(name) + <span class="string">"="</span>,</span><br><span class="line">			   cookieStart = <span class="built_in">document</span>.cookie.indexOf(cookieName),</span><br><span class="line">			   cookieValue = <span class="literal">null</span></span><br><span class="line">		<span class="keyword">if</span>(cookieStart &gt; <span class="number">-1</span>) &#123;</span><br><span class="line">			<span class="keyword">const</span> cookieEnd = <span class="built_in">document</span>.cookie.indexOf(<span class="string">";"</span>, cookieStart)</span><br><span class="line">			<span class="keyword">if</span>(cookieEnd == <span class="number">-1</span>) &#123;</span><br><span class="line">				cookieEnd = <span class="built_in">document</span>.cookie.length</span><br><span class="line">			&#125;</span><br><span class="line">			cookieValue = decodeURICompoent(<span class="built_in">document</span>.cookie.substring(cookieStart + cookieName.length, cookieEnd))	</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> cookieValue</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// set设置一个cookie</span></span><br><span class="line">	set: <span class="function"><span class="keyword">function</span>(<span class="params">name, value, expires, path, domain, secure</span>) </span>&#123;</span><br><span class="line">		<span class="keyword">var</span> cookieText = encodeURIComponet(name)+<span class="string">"="</span>+encodeURIComponet(value)</span><br><span class="line">		<span class="keyword">if</span>(expires <span class="keyword">instanceof</span> <span class="built_in">Date</span>) &#123;</span><br><span class="line">			cookieText += <span class="string">"; expires="</span> + expires.toGMTString()</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span>(path) &#123;</span><br><span class="line">			cookieText += <span class="string">";path="</span> + path</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span>(domain) &#123;</span><br><span class="line">			cookieText += <span class="string">"; domain"</span> + domain</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span>(secure) &#123;</span><br><span class="line">			cookieText += <span class="string">"; secure"</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">document</span>.cookie = cookieText</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 删除已有的cookie</span></span><br><span class="line">	unset: <span class="function"><span class="keyword">function</span>(<span class="params">name, path, domain, secure</span>) </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.set(name, <span class="string">""</span>, <span class="keyword">new</span> <span class="built_in">Date</span>(<span class="number">0</span>), path, domain, secure)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>是不是很麻烦，无论是获取一个cookie的值或是设置一个cookie都是很麻烦的事情，这也成为了后续的浏览器数据存储方案出现的一大原因。</p>
<h2 id="web存储"><a href="#web存储" class="headerlink" title="web存储"></a>web存储</h2><p>web存储机制最初作为HTML5的一部分被定义成API的形式，但又由于其本身的独特性与其他的一些原因而剥离了出来，成为独立的一个标准。web存储标准的API包括localStorage对象和seesionStorage对象。它所产生的主要原因主要出于以下两个原因：</p>
<ul>
<li>人们希望有一种在cookie之外存储回话数据的途径。</li>
<li>人们希望有一种存储大量可以跨会话存在的数据的机制。</li>
</ul>
<p>（注：其实在最初的web存储规范中包含了两种对象的定义：seesionStorage和globalStorage,这两个对象在支持这两个对象的浏览器中都是以windows对象属性的形式存在的）</p>
<h3 id="locaStorage"><a href="#locaStorage" class="headerlink" title="locaStorage"></a>locaStorage</h3><p>localStorage对象在修订过的HTML5规范中作为持久保存客户端数据的方案取代了我们上面所提到的globalStorage。从功能上来讲，我们可以通过locaStorage在浏览器端存储键值对数据，它相比于cookie而言，提供了更为直观的API，且在安全上相对好一点<br>，而且虽然localStorage只能存储字符串，但它也可以存储字符串化的JSON数据，因此相比于cookie，localStorage能存储更复杂的数据。总的来说相较于cookie，localStorage有以下优势：</p>
<ul>
<li>提供了简单明了的API来进行操作</li>
<li>更加安全</li>
<li>可储存的数据量更大</li>
</ul>
<p>也正是出于以上这些原因，localStorage被视为替代cookie的解决方案，但还是要注意不要在localStorage中存储敏感信息。</p>
<h4 id="localStorage的基本语法"><a href="#localStorage的基本语法" class="headerlink" title="localStorage的基本语法"></a>localStorage的基本语法</h4><p>localStorage的基本操作很简单，示例如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用方法存储数据</span></span><br><span class="line">localStorage.setItem(<span class="string">"name"</span>, <span class="string">"Srtian"</span>)</span><br><span class="line"><span class="comment">// 使用属性存储数据</span></span><br><span class="line">localStorage.say = <span class="string">"Hello world"</span></span><br><span class="line"><span class="comment">// 使用方法读取数据</span></span><br><span class="line"><span class="keyword">const</span> name = localStorage.getItem(<span class="string">"name"</span>)</span><br><span class="line"><span class="comment">// 使用属性读取数据</span></span><br><span class="line"><span class="keyword">const</span> say = localStorage.say</span><br><span class="line"><span class="comment">// 删除数据</span></span><br><span class="line">localStorage.removeItem(<span class="string">"name"</span>)</span><br></pre></td></tr></table></figure></p>
<p>但需要注意的是，我们上面的示例全是存储字符串格式的数据，当我们需要传输其他格式的数据时，我们就需要将这些数据全部转换为字符串格式，然后再进行存储：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> user = &#123;<span class="attr">name</span>:<span class="string">"Srtian"</span>, <span class="attr">age</span>: <span class="number">22</span>&#125;</span><br><span class="line">localStorage.setItem(<span class="string">"user"</span>, <span class="built_in">JSON</span>.stringify(user))</span><br></pre></td></tr></table></figure>
<p>当然，我们在获取值的时候也别忘了将其转化回来：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> age = <span class="built_in">JSON</span>.parse(localStorage.user)</span><br></pre></td></tr></table></figure>
<h4 id="localStorage储存数据的有效期与作用域"><a href="#localStorage储存数据的有效期与作用域" class="headerlink" title="localStorage储存数据的有效期与作用域"></a>localStorage储存数据的有效期与作用域</h4><p>通过locaStorage存储的数据时永久性的，除非我们使用removeItem来删除或者用户通过设置浏览器配置来删除，负责数据会一直保留在用户的电脑上，永不过期。</p>
<p>localStorage的作用域限定在文档源级别的（意思就是同源的才能共享），同源的文档间会共享localStorage的数据，他们可以互相读取对方的数据，甚至有时会覆盖对方的数据。当然，localStorage的作用域同样也受浏览器的限制。</p>
<h4 id="localStorage的兼容"><a href="#localStorage的兼容" class="headerlink" title="localStorage的兼容"></a>localStorage的兼容</h4><p>locaStorage的兼容如下表所示：</p>
<pre><code>    Feature     Chrome     Edge     Firefox (Gecko) Internet Explorer     Opera     Safari (WebKit)
localStorage     4     (Yes)        3.5                 8                  10.50     4
sessionStorage     5     (Yes)        2                 8                  10.50        4
</code></pre><h3 id="sessionStorage"><a href="#sessionStorage" class="headerlink" title="sessionStorage"></a>sessionStorage</h3><p>sessionStorage是web存储机制的另一大对象，sessionStorage 属性允许我们去访问一个 session Storage 对象。它与 localStorage 相似，不同之处在于 localStorage里面存储的数据没有过期时间设置，而Session Storage只存储当前会话页的数据，且只有当用户关闭当前会话页或浏览器时，数据才会被清除。</p>
<h4 id="sessionStorage的基本语法"><a href="#sessionStorage的基本语法" class="headerlink" title="sessionStorage的基本语法"></a>sessionStorage的基本语法</h4><p>我们可以通过下面的语法，来保存，获取，删除数据，大体语法与：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 保存数据到sessionStorage</span></span><br><span class="line">sessionStorage.setItem(<span class="string">'name'</span>, <span class="string">'Srtian'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从sessionStorage获取数据</span></span><br><span class="line"><span class="keyword">var</span> data = sessionStorage.getItem(<span class="string">'name'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从sessionStorage删除保存的数据</span></span><br><span class="line">sessionStorage.removeItem(<span class="string">'name'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从sessionStorage删除所有保存的数据</span></span><br><span class="line">sessionStorage.clear();</span><br></pre></td></tr></table></figure></p>
<p>下面的示例会自动保存一个文本输入框的内容，如果浏览器因偶然因素被刷新了，文本输入框里面的内容会被恢复，写入的内容不会丢失：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取文本输入框</span></span><br><span class="line"><span class="keyword">var</span> field = <span class="built_in">document</span>.getElementById(<span class="string">"field"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检测是否存在 autosave 键值</span></span><br><span class="line"><span class="comment">// (这个会在页面偶然被刷新的情况下存在)</span></span><br><span class="line"><span class="keyword">if</span> (sessionStorage.getItem(<span class="string">"autosave"</span>)) &#123;</span><br><span class="line">  <span class="comment">// 恢复文本输入框的内容</span></span><br><span class="line">  field.value = sessionStorage.getItem(<span class="string">"autosave"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 监听文本输入框的 change 事件</span></span><br><span class="line">field.addEventListener(<span class="string">"change"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 保存结果到 sessionStorage 对象中</span></span><br><span class="line">  sessionStorage.setItem(<span class="string">"autosave"</span>, field.value)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>在兼容性和优点方面，sessionStorage和localStorage是差不多的，因此在此也就不多说了，下面我们来聊一聊IndexedDB。</p>
<h2 id="IndexedDB"><a href="#IndexedDB" class="headerlink" title="IndexedDB"></a>IndexedDB</h2><p>虽然web存储机制对于存储较少量的数据非常便捷好用，但对于存储更大量的结构化数据来说，这种方法就不太满足开发者们的需求了。IndexedDB就是为了应对这个需求而产生的，它是由HTML5所提供的一种本地存储，用于在浏览器中储存较大数据结构的 Web API，并提供索引功能以实现高性能查找。它一般用于保存大量用户数据并要求数据之间有搜索需要的场景，当网络断开时，用户就可以做一些离线的操作。它较之SQL更为方便，不需要写一些特定的语法对数据进行操作，数据格式是JSON。</p>
<h3 id="IndexedDB的基本语法"><a href="#IndexedDB的基本语法" class="headerlink" title="IndexedDB的基本语法"></a>IndexedDB的基本语法</h3><p>使用IndexedDB在浏览器端存储数据会比上述的其他方法更为复杂。首先，我们需要创建数据库，并指定这个数据库的版本号：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意数据库的版本号只能是整数</span></span><br><span class="line"><span class="keyword">const</span> request = IndexedDB.open(databasename, version)</span><br></pre></td></tr></table></figure></p>
<p>然后我们需要生成处理函数，需要注意的是onupgradeneeded 是我们唯一可以修改数据库结构的地方。在这里面，我们可以创建和删除对象存储空间以及构建和删除索引。<br>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">request.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">	<span class="comment">// 创建数据库失败时的回调函数</span></span><br><span class="line">&#125;</span><br><span class="line">request.onsuccess = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">	<span class="comment">// 创建数据库成功时的回调函数</span></span><br><span class="line">&#125;</span><br><span class="line">request.onupgradeneededd = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">	 <span class="comment">// 当数据库改变时的回调函数</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后我们就可以建立对象存储空间了，对象存储空间仅调用 createObjectStore() 就可以创建。这个方法使用存储空间的名称，和一个对象参数。即便这个参数对象是可选的，它还是非常重要的，因为它可以让我们定义重要的可选属性和完善你希望创建的对象存储空间的类型。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">request.onupgradeneeded = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">	<span class="keyword">const</span> db = event.target.result</span><br><span class="line">	<span class="keyword">const</span> objectStore = db.createObjectStore(<span class="string">'name'</span>, &#123; <span class="attr">keyPath</span>:<span class="string">'id'</span> &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对象的存储空间我们已经建立好了，接下来我们就可以进行一系列的骚操作了，比如来个蛇皮走位！不不不，口误口误，比如添加数据：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">addData: <span class="function"><span class="keyword">function</span>(<span class="params">db, storename, data</span>) </span>&#123;</span><br><span class="line">	<span class="keyword">const</span> store = store = db.transaction(storename, <span class="string">'readwrite'</span>).objectStore(storename)</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; data.length; i++) &#123;</span><br><span class="line">		<span class="keyword">const</span> request = store.add(data[i])</span><br><span class="line">		request.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">			<span class="built_in">console</span>.error(<span class="string">'添加数据失败'</span>)</span><br><span class="line">		&#125;</span><br><span class="line">		request.onsuccess = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">			<span class="built_in">console</span>.log(<span class="string">'添加数据成功'</span>)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果我们想要修改数据，语法与添加数据差不多，因为重复添加已存在的数据会更新原本的数据，但还是有细小的差别：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">putData: <span class="function"><span class="keyword">function</span>(<span class="params">db, storename, data</span>) </span>&#123;</span><br><span class="line">	<span class="keyword">const</span> store = store = db.transaction(storename, <span class="string">'readwrite'</span>).objectStore(storename)</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; data.length; i++) &#123;</span><br><span class="line">		<span class="keyword">const</span> request = store.put(data[i])</span><br><span class="line">		request.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">			<span class="built_in">console</span>.error(<span class="string">'修改数据失败'</span>)</span><br><span class="line">		&#125;</span><br><span class="line">		request.onsuccess = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">			<span class="built_in">console</span>.log(<span class="string">'修改数据成功'</span>)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>获取数据：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">getDataByKey: <span class="function"><span class="keyword">function</span>(<span class="params">db, storename, key</span>) </span>&#123;</span><br><span class="line">	<span class="keyword">const</span> store = store = db.transaction(storename, <span class="string">'readwrite'</span>).objectStore(storename)</span><br><span class="line">	<span class="keyword">const</span> request = store.get(key)</span><br><span class="line">	request.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">		<span class="built_in">console</span>.error(<span class="string">'获取数据失败'</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	request.onsuccess = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">		<span class="keyword">const</span> result = e.target.result</span><br><span class="line">		<span class="built_in">console</span>.log(result)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>删除数据：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">deleteDate: <span class="function"><span class="keyword">function</span>(<span class="params">db, storename, key</span>) </span>&#123;</span><br><span class="line">	<span class="keyword">const</span> store = store = db.transaction(storename, <span class="string">'readwrite'</span>).objectStore(storename)</span><br><span class="line">	store.delete(key)</span><br><span class="line">	<span class="built_in">console</span>.log(<span class="string">'已删除存储空间'</span> + storename + <span class="string">'中的'</span> + key + <span class="string">'纪录'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>关闭数据库：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.close</span><br></pre></td></tr></table></figure>
<h3 id="IndexedDB的优点（相较于前面的存储方案）"><a href="#IndexedDB的优点（相较于前面的存储方案）" class="headerlink" title="IndexedDB的优点（相较于前面的存储方案）"></a>IndexedDB的优点（相较于前面的存储方案）</h3><ul>
<li>拥有更大的储存空间</li>
<li>能够处理更为复杂和结构化的数据</li>
<li>拥有更多的交互控制</li>
<li>每个’database’中可以拥有多个’database’和’table’</li>
</ul>
<h3 id="IndexedDB的局限性"><a href="#IndexedDB的局限性" class="headerlink" title="IndexedDB的局限性"></a>IndexedDB的局限性</h3><p>了解了IndexedDB的优点，我们当然也要来聊一聊IndexedDB的局限性与适用的场景：</p>
<h4 id="1-存储空间限制"><a href="#1-存储空间限制" class="headerlink" title="1. 存储空间限制"></a>1. 存储空间限制</h4><p>一个单独的数据库项目的大小没有限制。然而可能会限制每个 IndexedDB 数据库的大小。这个限制（以及用户界面对它进行断言的方式）在各个浏览器上也可能有所不同：</p>
<ul>
<li>Firefox: 对 IndexedDB 数据库的大小没有限制。在用户界面上只会针对存储超过 50 MB 大小的 BLOB（二进制大对象）请求权限。这个大小的限额可以通过 dom.indexedDB.warningQuota 首选项进行自定义。(定义在 <a href="http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/init/all.js)。" target="_blank" rel="noopener">http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/init/all.js)。</a></li>
<li>Google Chrome：<a href="https://developers.google.com/chrome...rage#temporary" target="_blank" rel="noopener">https://developers.google.com/chrome...rage#temporary</a><h4 id="2-兼容性问题"><a href="#2-兼容性问题" class="headerlink" title="2. 兼容性问题"></a>2. 兼容性问题</h4><img src="https://user-gold-cdn.xitu.io/2018/5/3/16325ce02ccb3e88?w=1240&amp;h=189&amp;f=png&amp;s=58377" alt="图片.png"><br>从上面的图我们可以看出对于IndexedDB的兼容来讲比前面所提及的存储方案要差不少，因此在使用IndexedDB时，我们也要好好的考虑兼容性的问题<h4 id="3-indexedDB受同源策略的限制"><a href="#3-indexedDB受同源策略的限制" class="headerlink" title="3. indexedDB受同源策略的限制"></a>3. indexedDB受同源策略的限制</h4>indexedDB使用同源原则，这意味着它把存储空间绑定到了创建它的站点的源（典型情况下，就是站点的域或是子域），所以它不能被任何其他源访问。要着重指出的一点是 IndexedDB 不适用于从另一个站点加载进框架的内容 (这是一项安全措施。详情请看这个：<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=595307" target="_blank" rel="noopener">https://bugzilla.mozilla.org/show_bug.cgi?id=595307</a></li>
</ul>
<p>除此之外，IndexedDB还存在诸如：不适合存储敏感数据，相较于web存储机制的操作更加复杂等问题，这都是我们在使用IndexedDB时需要考虑的。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/2.组件和元素/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/2.组件和元素/" itemprop="url">2.组件和元素</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:25:05+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React学习笔记/" itemprop="url" rel="index">
                    <span itemprop="name">React学习笔记</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  729
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  3
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="元素（element"><a href="#元素（element" class="headerlink" title="元素（element)"></a>元素（element)</h3><p>首先需要了解的就是React的元素，因为元素是React中最小的建造部件,你所看到的网页内容也就是由各种元素所组成的。React的元素在作用上来讲和HTML的DOM是一致的，但从本质上来看，React DOM是一种JavaScript的对象，它是通过一个树状结构的JS对象来模拟DOM树。</p>
<p>React DOM又称之为Virtual DOM，它在React中的作用很大。我们都知道React很快，很轻。而它之所以如此的原因便在于Virtual DOM。在React DOM内部会进行一套高效率的Diff算法，在应用的数据改变之后，React会尽力少地比较，然后根据虚拟DOM只改变真实DOM中需要被改变的部分。React也藉此实现了它的高效率，高性能。除此之外，react DOM还有其他的一些作用，独立分离出来的一层逻辑层，为react涉足于其他的领域提供了支持（譬如ReactNative,ReactVR 等）</p>
<p>React元素是不能突变的，也就是说不能更改它的子元素和任何属性，它的每一个元素都像电影里面的一频。更新 UI 的唯一方法是创建一个新的元素,并将其传入ReactDOM.render()方法。</p>
<h3 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h3><p>组件在官方文档的解释是：是指在UI界面中，可以被独立划分的、可复用的、独立的模块。</p>
<p>React 组件非常简单。我们可以认为它们就是简单的函数，接受 props 和 state (后面会讨论) 作为参数，然后渲染出 HTML。其实就类似于JS当中对function函数的定义，它一般会接收一个名为props的输入，然后返回相应的React元素，再交给ReactDOM，最后渲染到屏幕上。</p>
<p>最简单的创建一个组件的方法就是直接创建一个函数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 普通函数式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Welcome</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &lt;h1&gt;Hello, &#123;props.name&#125;&lt;/h1&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 箭头函数式</span></span><br><span class="line"><span class="keyword">const</span> Title = <span class="function"><span class="params">props</span> =&gt;</span> &lt;h1&gt;Hello, &#123;props.name&#125;&lt;<span class="regexp">/h1&gt;</span></span><br></pre></td></tr></table></figure>
<p>上面的写法被称之为函数式组件。同样的，我们也可以使用ES6中的class语法来定义一个组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Welcome</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">    <span class="keyword">return</span> &lt;h1&gt;Hello,&#123;this.props.name&#125;&lt;/h1&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而组件在定义好之后，可以通过JSX描述的方式被引用，组件之间也可以相互嵌套和组合。<br>（<em>有一个需要注意的是</em>：组件名称总是以大写字母开始。<br>举例来说, <button>代表一个 DOM 标签，而 <welcome> 则代表一个组件，并且需要在作用域中有一个 Welcome组件。）</welcome></button></p>
<p>而 React 当中的组件是通过嵌套或组合的方式来实现组件代码复用的。通过<br>props传值和组合使用组件几乎可以满足所有场景下的需求。而这种方法也更符合组件化的思想。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/1.JSX初识/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/1.JSX初识/" itemprop="url">1.JSX初识</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:25:05+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React学习笔记/" itemprop="url" rel="index">
                    <span itemprop="name">React学习笔记</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  651
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  3
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>JSX 让你可以用 HTML 语法去写 JavaScript 函数调用 。JSX就是一种语法扩展，从本质上讲，JSX只是为React.createElement (component, props, …children)函数提供的语法糖。它不属于JS，也不属于HTML，它在React中也是可选的。</p>
<p>JSX的语法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyButton color=<span class="string">"blue"</span> shadowSize=&#123;<span class="number">2</span>&#125;&gt;</span><br><span class="line">  Click Me</span><br><span class="line">&lt;<span class="regexp">/MyButton&gt;</span></span><br></pre></td></tr></table></figure>
<p>转译后：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">React.createElement(</span><br><span class="line">  MyButton,</span><br><span class="line">  &#123;<span class="attr">color</span>: <span class="string">'blue'</span>, <span class="attr">shadowSize</span>: <span class="number">2</span>&#125;,</span><br><span class="line">  <span class="string">'Click Me'</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>JSX一般来说需要进行转译，才能为浏览器所识别。而且JSX也和HTML有很多相似的地方，比如使用class和嵌套还有在JSX中可以使用javascript的表达式，但使用他们的时候有以下几点需要注意：</p>
<ul>
<li><p>在使用class时，在JSX中需要使用的是className。而且比起 HTML ， JSX 更接近于 JavaScript ， 所以 React DOM 使用驼峰(camelCase)属性命名约定, 而不是HTML属性名称</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> title = (</span><br><span class="line">&lt;div&gt;</span><br><span class="line">&lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">&lt;p&gt;Let's learn JSX&lt;/</span>p&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>在嵌套时，首先一般需要在最外层加上小括号将所有元素全部包裹起来。（如上）<br>其次需要注意的是JSX在嵌套时，最外层有且只能有一个标签，否则就会出错袄。</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这是一个错误的实例，需要注意</span></span><br><span class="line"><span class="keyword">const</span> title = (</span><br><span class="line">&lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">&lt;p&gt;Let's learn JSX&lt;/</span>p&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<ul>
<li>JSX中同样也可以使用javascript的表达式，在使用表达式时要注意的是需要在最外层加上大括号才行。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayhi</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="string">'Hello world'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> title = (</span><br><span class="line">&lt;div&gt;</span><br><span class="line">&lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">&lt;p&gt;Let's learn JSX. &lt;span&gt;&#123;sayhi()&#125;&lt;/</span>span&gt;&lt;/p&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
<p>JSX也有利于解决安全方面的问题。在渲染之前, React DOM 会格式化(escapes) JSX中的所有值. 从而保证用户无法注入任何应用之外的代码.在被渲染之前，所有的数据都被转义成为了字符串处理。 以避免 XSS(跨站脚本) 攻击。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> title = response.potentiallyMaliciousInput;</span><br><span class="line"><span class="comment">// 安全的:</span></span><br><span class="line"><span class="keyword">const</span> element = &lt;h1&gt;&#123;title&#125;&lt;/h1&gt;;</span><br></pre></td></tr></table></figure></p>
<p>但由于JSX是可选的，所以在此我们也来看一下没有JSX的React。我们也可以通过 React.createElement 来创建一个树。它接受三个参数，第一个参数是标签，第二个参数是一个属性对象，第三个是子节点。如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> child1 = React.createElement(<span class="string">'li'</span>, <span class="literal">null</span>, <span class="string">'First Text Content'</span>);</span><br><span class="line"><span class="keyword">var</span> child2 = React.createElement(<span class="string">'li'</span>, <span class="literal">null</span>, <span class="string">'Second Text Content'</span>);</span><br><span class="line"><span class="keyword">var</span> root = React.createElement(<span class="string">'ul'</span>, &#123; <span class="attr">className</span>: <span class="string">'my-list'</span> &#125;, child1, child2);</span><br><span class="line">ReactDOM.render(root, <span class="built_in">document</span>.getElementById(<span class="string">'example'</span>));</span><br></pre></td></tr></table></figure>
<p>但总体而言使用JSX，会让代码量减少，让代码显得跟优雅，且可以使可读性与逻辑性更强。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/React高阶组件的那些事，了解一下？/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/React高阶组件的那些事，了解一下？/" itemprop="url">React高阶组件的那些事，了解一下？</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:25:05+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React/" itemprop="url" rel="index">
                    <span itemprop="name">React</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  3,898
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  15
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>学习react已经有一段时间了，期间在阅读官方文档的基础上也看了不少文章，但感觉对很多东西的理解还是不够深刻，因此这段时间又在撸一个基于react全家桶的聊天App（现在还在瞎78写的阶段，在往这个聊天App这个方向写），通过实践倒是对react相关技术栈有了更为深刻的理解。在使用react-redux的过程中，发现connect好像还挺有意思的，也真实感受到了高阶组件所带来的便利，出于自己写项目本身就是为了学习的目的，因此对高阶组件又进行了一番学习。写下这篇文章主要是对高阶组件的知识进行一个梳理与总结，如有错误疏漏之处，敬请指出，不胜感激。</p>
<h2 id="初识高阶组件"><a href="#初识高阶组件" class="headerlink" title="初识高阶组件"></a>初识高阶组件</h2><p><em>要学习高阶组件首先我们要知道的就是高阶组件是什么，解决了什么样的问题。</em></p>
<p>React官方文档的对高阶组件的说明是这样的:</p>
<blockquote>
<p>A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, perse. They are a pattern that emerges from React’s compositional nature.</p>
</blockquote>
<p>从上面的说明我们可以看出，react 的高阶组件并不是 react API 的一部分，它源自于 react 的生态。</p>
<p>简单来说，一个高阶组件就是一个函数，它接受一个组件作为输入，然后会返回一个新的组件作为结果，且所返回的新组件会进行相对增强。值得注意的是，我们在这说的组件并不是组件实例，而是一个组件类或者一个无状态组件的函数。就像这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">removeUserProp</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line"><span class="comment">//WrappingComponent这个组件名字并不重要，它至少一个局部变量，继承自React.Component</span></span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">WrappingComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line"><span class="comment">// ES6的语法，可以将一个对象的特定字段过滤掉</span></span><br><span class="line">            <span class="keyword">const</span> &#123;user, ...otherProps&#125; = <span class="keyword">this</span>.props</span><br><span class="line">            <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...otherProps</span>&#125; /&gt;</span></span></span><br><span class="line"><span class="xml">        &#125;</span></span><br><span class="line"><span class="xml">      &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>了解设计模式的大佬们应该发现了，它其实就是的设计模式中的装饰者模式在react中的应用，它通过组合的方式从而达到很高的灵活程度和复用。<br>就像上面的代码，我们定义了一个叫做 removeUserProp 的高阶组件，传入一个叫做 WrappedComponent 的参数（代表一个组件类），然后返回一个新的组件 ，新的组件与原组件并没有太大的区别，只是将原组件中的 prop 值 user 给剔除了出来。</p>
<p>有了上面这个高阶组件的，当我们不希望某个组件接收到 user 时，我们就可以将这个组件作为参数传入 removeUserProp() 函数中，然后使用这个返回的组件就行了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> NewComponent = removeUserProp(OldComponent)</span><br></pre></td></tr></table></figure>
<p>这样 NewComponent 组件与 OldComponent 组件拥有完全一样的行为，唯一的区别就在于传入的name属性对这个组件没有任何作用，它会自动屏蔽这个属性。也就是说，我们这个高阶组件成功的为传入的组件增加了一个屏蔽某个prop的功能。</p>
<p>那么明白了什么是高阶组件后，我们接下来要做的是，弄清楚高阶组件主要解决的问题，或者说我们为什么需要高阶组件？总结起来主要是以下两个方面：</p>
<ol>
<li>代码重用<blockquote>
<p>在很多情况下，react组件都需要共用同一个逻辑，我们在这个时候就可以把这部分共用的逻辑提取出来，然后利用高阶组件的形式将其组合，从而减少很多重复的组件代码。</p>
</blockquote>
</li>
</ol>
<p>2.修改React组件的行为</p>
<blockquote>
<p>很多时候有些现成的react组件并不是我们自己撸出来的，而是来自于GitHub上的大佬们的开源贡献，而当我们要对这些组件进行复用的时候，我们往往都不想去触碰这些组件的内部逻辑，这时我们就能通过高阶组件产生新的组件满足自身需求，同时也对原组件没有任何损害。</p>
</blockquote>
<p>现在我们对高阶组件有了一个较为直观的认识，知道了什么是高阶组件以及高阶组件的主要用途。接下来我们就要具体了解高阶组件的实现方式以及它的具体用途了。</p>
<h3 id="高阶组件的实现分类"><a href="#高阶组件的实现分类" class="headerlink" title="高阶组件的实现分类"></a>高阶组件的实现分类</h3><p>对于高阶组件的实现方式我们可以根据作为参数传入的组件与返回的新组件的关系将高阶组件的实现方式分为以下两大类：</p>
<ul>
<li>代理方式的高阶组件</li>
<li>继承方式的高阶组件<h2 id="代理方式的高阶组件"><a href="#代理方式的高阶组件" class="headerlink" title="代理方式的高阶组件"></a>代理方式的高阶组件</h2>从高阶组件的使用频率来讲，我们使用的绝大多数的高阶组件都是代理方式的高阶组件，如react-redux中的connect，还有我们在上面所实现的那个removeUserProp。这类高阶组件的特点是返回的新组件类直接继承于 React.Component 类。新组建在其中扮演的角色是一个传入参数组件的代理，在新组建的render函数中，把被包裹的组件渲染出来。在此过程中，除了高阶组件自己需要做的工作，其他的工作都会交给被包裹的组件去完成。</li>
</ul>
<p>代理方式的高阶组件具体而言，应用场景可以分为以下几个：</p>
<ul>
<li>操作prop</li>
<li>通过ref获取组件实例</li>
<li>抽取状态</li>
<li>包装组件</li>
</ul>
<h3 id="控制prop"><a href="#控制prop" class="headerlink" title="控制prop"></a>控制prop</h3><p>代理类型的高阶组件返回的新组件时，渲染过程也会被新组建的render函数所控制，而在此过程中，render函数相对于一个代理，完全决定该如何使用被包裹在其中的组件。在render函数中，this.props包含了新组件接受到的所有prop。因此最直观的用法就是接受到props，然后进行任何读取，增减，修改等控制props的自定义操作。<br>就比如我们上面的那个示例，就做到了删除prop的功能，当然我们也能实现一个添加prop的高阶组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addNewProp</span>(<span class="params">WrappedComponent, newProps</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">WrappingComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...thisProps</span>&#125; &#123;<span class="attr">...newProps</span>&#125; /&gt;</span></span></span><br><span class="line"><span class="xml">        &#125;</span></span><br><span class="line"><span class="xml">      &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>这个addNewProp高阶组件与我们最开始举例的removeUserProp高阶组件在实现上并无太大的区别。唯一区别较大的就是我们传入的参数除了WrappedComponent组件类外，还新增了newProps参数。这样的高阶组件在复用性方面会跟友好，我们可以利用这样一个高阶组件给不同的组件添加不同的新属性，比如这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> FirstComponent = addNewProp(OldComponent,&#123;<span class="attr">num</span>: First&#125;)</span><br><span class="line"><span class="keyword">const</span> LastComponent = addNewProp(NewComponent,&#123;<span class="attr">num</span>: Last&#125;)</span><br></pre></td></tr></table></figure>
<p>在上面的代码中，我们实现了让两个完全不同的组件分别通过高阶组件生成了两个完成不同的新的组件，而这其中唯一相同的是都添加了一个属性值，且这个属性还不相同。从上面的代码我们也不难发现，高阶组件可以重用在不同组件上，减少了重复的代码。当需要注意的是，在修改和删除 Props的时候，除非由特殊的要求，否则最好不要影响到原本传递给普通组件的 Props。</p>
<h3 id="通过ref获取组件实例"><a href="#通过ref获取组件实例" class="headerlink" title="通过ref获取组件实例"></a>通过ref获取组件实例</h3><p>我们可以通过ref获取组件实例，但值得注意的是，React官方不提倡访问ref，我们只是讨论一下这个技术的可行性。在此我们写一个refsHOC的高阶组件，可以获得被包裹组件的ref，从而根据ref直接操纵被包裹组件的实例：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">refsHOC</span>(<span class="params">WrappedComponent</span>) =&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">HOCComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">super</span>(...arguments)</span><br><span class="line">      <span class="keyword">this</span>.linkRef = <span class="keyword">this</span>.linkRef.bind(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    linkRef(wrappedInstance) &#123;</span><br><span class="line">      <span class="keyword">this</span>._root = wrappedInstance</span><br><span class="line">    &#125;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> props = &#123;...this.props, <span class="attr">ref</span>: <span class="keyword">this</span>.linkRef&#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...props</span>&#125;/&gt;</span></span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">export default refsHOC</span></span><br></pre></td></tr></table></figure>
<p>这个refs高阶组件的工作原理其实也是增加传递给被包裹组件的props，不同的是利用了ref这个特殊的prop而已。我们通过linkRef来给被包裹组件传递ref值，linkRef被调用时，我们就可以得到被包裹组件的DOM实例。</p>
<p>这种高阶组件在用途上来讲可以说是无所不能的，因为只要能够获得对被包裹组件的引用，就能通过这个引用任意操纵一个组件的DOM元素，贼酸爽。但它从某个角度来讲也是啥也干不了的，因为react团队表示：<strong>不要过度使用 Refs</strong>。且我们也有更好的替代品——控制组件（Controlled Component)来解决相关问题，因此这个坑建议大家还是尽量少踩为好。</p>
<h3 id="抽取状态"><a href="#抽取状态" class="headerlink" title="抽取状态"></a>抽取状态</h3><p>对于抽取状态，我想大家应该都不会很陌生。react-redux中的connect函数就实现了这种功能，它异常的强大，也成功吸引了我对高阶组件的注意。但在这有一点需要明确的是：connect函数本身并不是高阶组件，connect函数执行的结果才是一个高阶组件。让我们来看看connect的源码的主要逻辑：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">connect</span>(<span class="params">mapStateToProps, mapDispatchToProps, mergeProps, options = &#123;&#125;</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">wrapWithConnect</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">        <span class="class"><span class="keyword">class</span> <span class="title">Connect</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">            <span class="keyword">constructor</span>(props, context) &#123;</span><br><span class="line">                <span class="comment">//参数获取</span></span><br><span class="line">                <span class="keyword">super</span>(props, context)</span><br><span class="line">                <span class="keyword">this</span>.store = props.store || context.store</span><br><span class="line">                <span class="keyword">const</span> storeState = <span class="keyword">this</span>.store.getState()</span><br><span class="line">                <span class="keyword">this</span>.state = &#123; storeState &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 进行判断，当数据发生改变时，Component重新渲染</span></span><br><span class="line">            shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">                <span class="keyword">if</span> (propsChanged || mapStateProducedChange || dispatchPropsChanged) &#123;</span><br><span class="line">                 <span class="keyword">this</span>.updateState(nextProps)</span><br><span class="line">                  <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">                 &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            <span class="comment">// 改变Component中的state</span></span><br><span class="line">            componentDidMount() &#123;</span><br><span class="line">                 <span class="keyword">this</span>.store.subscribe(() = &#123;</span><br><span class="line">                  <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">                   storeState: <span class="keyword">this</span>.store.getState()</span><br><span class="line">                  &#125;)</span><br><span class="line">                 &#125;)</span><br><span class="line">                &#125;</span><br><span class="line">            render()&#123;</span><br><span class="line">                <span class="keyword">this</span>.renderedElement = createElement(WrappedComponent,</span><br><span class="line">                    <span class="keyword">this</span>.mergedProps</span><br><span class="line">                )</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">this</span>.renderedElement</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> hoistStatics(Connect, WrappedComponent)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>从上面的代码我们不难看出connect模块的返回值wrapWithConnect是一个函数，而这个函数才是我们所认知的高阶组件。wrapWithConnect函数会返回一个ReactComponent对象Connect，Connect会重新render外部传入的原组件WrappedComponent，并把connect中所传入的mapStateToProps, mapDispatchToProps和this.props合并后结合成一个对象，通过属性的方式传给WrappedComponent，这才是最终的渲染结果。</p>
<h3 id="包装组件"><a href="#包装组件" class="headerlink" title="包装组件"></a>包装组件</h3><p>在日常开发中我们所接触到的大多数的高阶组件都是通过修改props部分来对输入的组件进行相对增强的。但其实高阶组件还有其他的方式来增强组件，比如我们可以通过在render函数中的JSX引入其他元素，甚至将多个react组件合并起来，来获得更骚气的样式或方法，例如我们可以给组件增加style来改变组件样式：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> styleHOC = <span class="function">(<span class="params">WrappedComponent, style</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">HOCComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line">            <span class="keyword">return</span> (</span><br><span class="line">            &lt;div style=&#123;style&#125;&gt;</span><br><span class="line">                &lt;WrappedComponent &#123;...this.props&#125; /&gt;</span><br><span class="line">            &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">            )</span></span><br><span class="line"><span class="regexp">        &#125;</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure></p>
<p>当我们想改变组件的样式的时候，我们就可以直接调用这个函数，比如这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> style = &#123;</span><br><span class="line">			background-color: #f1fafa;</span><br><span class="line">			font-family: <span class="string">"微软雅黑"</span>;</span><br><span class="line">			font-size: <span class="number">20</span>px;</span><br><span class="line">		&#125;</span><br><span class="line"><span class="keyword">const</span> BeautifulComponent = styleHOC(uglyComponent, style)</span><br></pre></td></tr></table></figure>
<h2 id="继承方式的高阶组件"><a href="#继承方式的高阶组件" class="headerlink" title="继承方式的高阶组件"></a>继承方式的高阶组件</h2><p>前面我们讨论了代理方式实现的高阶组件以及它们的主要使用方式，现在我们继续来讨论一下以继承方式实现的高阶组件。</p>
<p>。继承方式的高阶组件通过继承来关联作为参数传入的组件和返回的组件，比如传入的组件参数是OldComponent,那函数所返回的组件就直接继承于OldComponemt。</p>
<p>码界有句老话说的好：组合优于继承。在高阶组件里也不例外。<br>继承方式的高阶组件相对于代理方式的高阶组件有很多不足之处，比如输入的组件与输出的组件共有一个生命周期等，因此通常我们接触到的高阶组件大多是代理方式实现的高阶组件，也推荐大家首先考虑以代理方式来实现高阶组件。但我们还是需要去了解并学习它，毕竟它也是有可取之处的，比如在操作生命周期函数上它还是具有其优越性的。</p>
<h3 id="操作生命周期函数"><a href="#操作生命周期函数" class="headerlink" title="操作生命周期函数"></a>操作生命周期函数</h3><p>说继承方式的高阶组件在操纵生命周期函数上有其优越性其实不够说明它在这个领域的地位，更准确地表达是：操作生命周期函数是继承方式的高阶组件所特有的功能。这是由于继承方式的高阶组件返回的新组件继承于作为参数传入的组件，两个组件的生命周期是共用的，因此可以重新定义组件的生命周期函数并作用于新组件。而代理方式的高阶组件作为参数输入的组件与输出的组件完全是两个生命周期，因此改变生命周期函数也就无从说起了。</p>
<p>例如我们可以定义一个让参数组件只有在用户登录时才显示的高阶组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> shouldLoggedInHOC = <span class="function">(<span class="params">WrappedComponent</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">MyComponent</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">this</span>.props.loggedIn) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">super</span>.render()</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="操纵Prop"><a href="#操纵Prop" class="headerlink" title="操纵Prop"></a>操纵Prop</h3><p>除了操作生命周期函数外，继承方式的高阶函数也能对Prop进行操作，但总的难说贼麻烦，当然也有简单的方式，比如这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">removeProps</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">NewComponent</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line">        <span class="keyword">const</span>&#123; user, ...otherProps &#125; = <span class="keyword">this</span>.props</span><br><span class="line">        <span class="keyword">this</span>.props = otherProps</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.render()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>虽然这样看起来很简单，但我们直接修改了this.props，这不是一个好的实践，可能会产生不可预料的后果，更好的操作办法是这样的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">removeProps</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="title">NewComponent</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">        render() &#123;</span><br><span class="line">        <span class="keyword">const</span> element =<span class="keyword">super</span>.render()</span><br><span class="line">        <span class="keyword">const</span>&#123; user, ...otherProps &#125; = <span class="keyword">this</span>.props</span><br><span class="line">        <span class="keyword">this</span>.props = otherProps</span><br><span class="line">        <span class="keyword">return</span> React.cloneElement(element, <span class="keyword">this</span>.props, element.props.children)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们可以通过React.cloneElement来传入新的props，让这些产生的组件重新渲染一次。但虽然这种方式可以解决直接修改this.props所带来的问题，但实现起来贼麻烦，唯一用得上的就是高阶组件需要根据参数组件WrappedComponent渲染结果来决定如何修改props时用得上，其他的时候显然使用代理模式更便捷清晰。</p>
<h2 id="高阶组件命名"><a href="#高阶组件命名" class="headerlink" title="高阶组件命名"></a>高阶组件命名</h2><p>用 HOC 包裹了一个组件会使它失去原本 WrappedComponent 的名字，可能会影响开发和debug。</p>
<p>因此我们通常会用 WrappedComponent 的名字加上一些 前缀作为 HOC 的名字。我们来看看React-Redux是怎么做的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getDisplayName</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> WrappedComponent.displayName ||</span><br><span class="line">         WrappedComponent.name ||</span><br><span class="line">         ‘Component’</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HOC.displayName = <span class="string">`HOC(<span class="subst">$&#123;getDisplayName(WrappedComponent)&#125;</span>)`</span></span><br><span class="line"><span class="comment">//或</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HOC</span> <span class="keyword">extends</span> ... </span>&#123;</span><br><span class="line">  <span class="keyword">static</span> displayName = <span class="string">`HOC(<span class="subst">$&#123;getDisplayName(WrappedComponent)&#125;</span>)`</span></span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>实际上我们不用自己来写getDisplayName这个函数，recompose 提供了这个函数，我们只要使用即可。</p>
<h4 id="结尾语"><a href="#结尾语" class="headerlink" title="结尾语"></a>结尾语</h4><p>我们其他要注意的就是<a href="https://react.bootcss.com/react/docs/higher-order-components.html" target="_blank" rel="noopener">官方文档</a>所说的几个约定与相关规范，在此我就不一一赘述了，感兴趣的可以自己去看看。最后很感谢能看到这里的朋友，因为水平有限，如果有错误敬请指正，十分感激！</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/4.表单组件/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/4.表单组件/" itemprop="url">4.表单组件</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:25:05+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React学习笔记/" itemprop="url" rel="index">
                    <span itemprop="name">React学习笔记</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  713
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  3
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>在React组件中，表单组件与其他的组件时不一样的。</p>
<p>表单组件支持几个受用户交互影响的属性：</p>
<ul>
<li>value。用于 input、textarea 组件。</li>
<li>checked。用于类型为 checkbox 或者 radio 的 input 组件。</li>
<li>selected。用于 option 组件。</li>
</ul>
<p>表单组件可以通过onChange回调函数来监听组件变化。当用户做出以下交互时，onChange 执行并通过浏览器做出响应：</p>
<ul>
<li>input 或 textarea 的 value 发生变化时。</li>
<li>input 的 checked 状态改变时。</li>
<li>option 的 selected 状态改变时。</li>
</ul>
<h6 id="受控组件与非受控组件"><a href="#受控组件与非受控组件" class="headerlink" title="受控组件与非受控组件"></a>受控组件与非受控组件</h6><p>接下来我们来聊一聊受控组件与非受控组件。受控组件的值由props或state传入，用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件，我们才能在页面中看到元素中值的变化，假如组件没有绑定事件处理函数改变state，用户的输入是不会起到任何效果的，这也就是“受控”的含义所在。</p>
<p>而非受控组件则类似于传统的DOM表单控件，用户输入不会直接引起应用state的变化，我们也不会直接为非受控组件传入值。想要获取非受控组件，我们需要使用一个特殊的ref属性，同样也可以使用defaultValue属性来为其指定一次性的默认值。</p>
<p>而在表单组件中，我们一般都需要来响应用户的输入，会有表单组件的出发和数据的处理。因此，我们在表单组件中一般都是使用受控组件，这样可以保证表单的数据在组件的state管理之下，而不是各自独立保有各自的数据。</p>
<h6 id="受控组件"><a href="#受控组件" class="headerlink" title="受控组件"></a>受控组件</h6><p>接下来，我们先来延时以下如何定义一个受控组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">render: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">value</span>=<span class="string">"Hello，world!"</span> /&gt;</span>;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br></pre></td></tr></table></figure>
<p>一个受控的input有一个value的prop。在这个input中，用户的输入并不会对其产生影响，因为React已经有了声明value值了。当我们需要对其进行改变的时候我们就需要onChange事件了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">getInitialState: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> &#123;<span class="attr">value</span>: <span class="string">'Hello!'</span>&#125;;</span><br><span class="line"> &#125;,</span><br><span class="line"> handleChange: <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">   <span class="keyword">this</span>.setState(&#123;<span class="attr">value</span>: event.target.value&#125;);</span><br><span class="line"> &#125;,</span><br><span class="line"> render: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (</span><br><span class="line">     &lt;input</span><br><span class="line">       type=<span class="string">"text"</span></span><br><span class="line">       value=&#123;<span class="keyword">this</span>.state.value&#125;</span><br><span class="line">       onChange=&#123;<span class="keyword">this</span>.handleChange&#125;</span><br><span class="line">     /&gt;</span><br><span class="line">   );</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<h6 id="非受控组件"><a href="#非受控组件" class="headerlink" title="非受控组件"></a>非受控组件</h6><p>相反的不使用value值就是非受控组件：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">render: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span>/&gt;</span>;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>上面的代码将渲染出一个空值的输入框，用户输入将立即反应到元素上。和受控元素一样，使用 onChange 事件可以监听值的变化。</p>
<p>不受控组件维持它自己的内部状态。而受控组件不畏吃它自己的内部状态，它基于prop来渲染。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/05/06/3.组件数据/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/05/06/3.组件数据/" itemprop="url">3.组件数据</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-06T13:25:05+08:00">
                2018-05-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React学习笔记/" itemprop="url" rel="index">
                    <span itemprop="name">React学习笔记</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,229
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  5
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p> React的组件数据主要分为两种：</p>
<ul>
<li>prop</li>
<li>state</li>
</ul>
<p>那么怎么选择数据的类型呢？其实选择的原则很简单，prop是组件的对外接口，state是组件的内部状态，对外用prop，内部用state。</p>
<h3 id="prop（property）"><a href="#prop（property）" class="headerlink" title="prop（property）"></a>prop（property）</h3><p> React 中，prop是从外部传递给组件的数据，一个 React 组件通过定义自己能够接受的prop就定义了自己的对外公共接口。每个 React 组件都是一个独立的模块，组件之外的一切都时外部世界，外部世界就是通过prop来和组件对话的。</p>
<p>在形式上，prop之于JSX就相当于attributes之于HTML。从写法上来看呢，我们为组件传入<br>props就像为HTML标签添加属性一样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> SimpleButton = <span class="function"><span class="params">props</span> =&gt;</span></span><br><span class="line">&#123;<span class="comment">/*&lt;button style=&#123;&#123; color: props.color &#125;&#125;&gt;click&lt;/button&gt;*/</span>&#125;</span><br><span class="line">&lt;button className=&#123;props.color&#125;&gt;click&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">ReactDOM.render(&lt;SimpleButton color="red" /</span>&gt;, <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>))</span><br></pre></td></tr></table></figure>
<p>所得的HTML代码是：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;button <span class="class"><span class="keyword">class</span></span>=<span class="string">"red"</span>&gt;click&lt;<span class="regexp">/button&gt;</span></span><br></pre></td></tr></table></figure>
<p>在概念上，props对于组件就相当于JS中参数之于函数。我们可以抽象出这样一个函数来解释：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Component(props) = View</span><br></pre></td></tr></table></figure>
<p>组件函数通过接受props进而来显现视图的内容。</p>
<p>值得注意的是 React 组件的prop搜能支持的类型十分丰富，JS所支持的数据类型它都支持。但当prop的类型不是字符串类型是。在JSX中必须用花括号{}把prop值包住。如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;SampleButton</span><br><span class="line">    id=<span class="string">"sample"</span> borderWidth=&#123;<span class="number">2</span>&#125; onClick=&#123;onButtonClick&#125;</span><br><span class="line">    style=&#123;&#123;<span class="attr">color</span>: <span class="string">"red"</span>&#125;&#125;</span><br><span class="line">/&gt;</span><br></pre></td></tr></table></figure>
<p>style属性有两个花括号，外层的花括号代表的是JSX的语法，内层的花括号代表这是一个对象常量。</p>
<h4 id="prop是只读的"><a href="#prop是只读的" class="headerlink" title="prop是只读的"></a>prop是只读的</h4><p>在React中，prop都是自上而下的，从父组件传递到子组件。并且prop是读的，也就是说我们不能在组件中直接修改prop的内容。组件只能通过传入的prop来进行对页面的渲染，不能去显式的更改prop。</p>
<h4 id="prop数据类型检查"><a href="#prop数据类型检查" class="headerlink" title="prop数据类型检查"></a>prop数据类型检查</h4><p>由于prop是组件的对外接口，那么应该对传入的prop数据进行检查。对此在ES6方法定义组件类中，可以通过增加类的propTypes属性来定义prop规格，这不是一种声明而是一种限制。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Counter.propTypes = &#123;</span><br><span class="line">    caption: PropTypes.string.isRequired;</span><br><span class="line">    initValue: PropTypes.number</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>在上面的代码中我们队caption与InitValue都进行了限制。其中caption要求是字符串与一定需要被指定的；而intivalue则要求是数字。</p>
<p>还有一个要注意的是定义类的propTypes属性一般只用于开发阶段，不用于产品阶段。因为定义时会占用一些代码空间，而且propTypes检查也需要消耗CPU进行计算。因此最佳实践就是在开发时使用propTypes属性，以避免出现错误，但在发布产品时使用自动的方法去掉。</p>
<h4 id="prop默认值"><a href="#prop默认值" class="headerlink" title="prop默认值"></a>prop默认值</h4><p>由于prop是只读的，我们不能直接为prop赋值。React专门准备了一个方法定义prop的默认值，那就是使用defaultProps。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> PropTypes <span class="keyword">from</span> <span class="string">'prop-types'</span></span><br><span class="line"><span class="keyword">const</span> Title = <span class="function"><span class="params">props</span> =&gt;</span> &lt;h1&gt;&#123;props.title&#125;&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">Title.defaultProps = &#123;</span></span><br><span class="line"><span class="regexp">title: 'Wait for parent to pass props.'</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">Title.propTypes = &#123;</span></span><br><span class="line"><span class="regexp">title: PropTypes.string.isRequired</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="state"><a href="#state" class="headerlink" title="state"></a>state</h3><p>驱动React组件来渲染组件的除了prop，还有state,如果要翻译state的话可以翻译为状态，而state也代表了组件的内部状态，由于组件不能直接修改传入的prop，因此需要记录自身的数据变化的时候需要使用state。</p>
<p>一般而言都是在constructor对组件state进行初始化。之后就可以在组件中通过this.state来访问它，既然是state那么肯定涉及到数据的改变，因此我们还需额外定义一个负责处理state变化的函数，这样的函数中一般都会包含this.setState这<br>个方法。</p>
<p>要注意的是组件的state必须是一个javascript对象，不能是string或者number这样简单的数据类型。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Counter</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line"><span class="keyword">constructor</span>(props) &#123;</span><br><span class="line"><span class="keyword">super</span>(props)</span><br><span class="line"><span class="keyword">this</span>.state = &#123;</span><br><span class="line">counter: <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">addOne() &#123;</span><br><span class="line"><span class="keyword">this</span>.setState(<span class="function">(<span class="params">prevState</span>) =&gt;</span>(&#123;</span><br><span class="line">counter: prevState.counter + <span class="number">1</span></span><br><span class="line">&#125;))</span><br><span class="line">&#125;</span><br><span class="line">render() &#123;</span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line">&lt;div&gt;</span><br><span class="line">&lt;p&gt;&#123; <span class="keyword">this</span>.state.counter &#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">&lt;button</span></span><br><span class="line"><span class="regexp">onClick=&#123;() =&gt; this.addOne()&#125;&gt;</span></span><br><span class="line"><span class="regexp">Increment</span></span><br><span class="line"><span class="regexp">&lt;/</span>button&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>react被称为一个单向数据流的状态机，所以说在整个react的学习中，state是贯穿全程的，理解state是写好react的基础。好像提到state就要与props联系起来似的，其实他们是两个完全不同的东西，props在于数据的传递，state在于当前组件的状态。他们主要有以下的区别：</p>
<ul>
<li>prop用于定义外部接口，state用于记录内部状态。</li>
<li>prop的赋值在外部世界使用组件时，state的赋值在组件内部。</li>
<li>组件不应该改变prop的值，而state存在的目的就是让组件来改变。</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blog/page/4/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/blog/">1</a><span class="space">&hellip;</span><a class="page-number" href="/blog/page/4/">4</a><span class="page-number current">5</span><a class="page-number" href="/blog/page/6/">6</a><a class="extend next" rel="next" href="/blog/page/6/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/blog/images/avatar.jpg"
                alt="Srtian" />
            
              <p class="site-author-name" itemprop="name">Srtian</p>
              <p class="site-description motion-element" itemprop="description">JUST DO IT.</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">59</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">15</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">37</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://gitee.com/srtian96" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="shenruotian@gmail.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Srtian</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">102.8k</span>
  
</div>





        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      本站访客数
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      人次
    </span>
  

  
    <span class="site-pv">
      本站总访问量
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      次
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/blog/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/schemes/pisces.js?v=5.1.4"></script>



  

  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/blog/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  
  

  

  

  

</body>
</html>
